Corrected file test function to use gerb_file operations a good way.
[geda-gerbv/spe.git] / src / drill.c
blobd2fc9d893c0e2b60cffbb8aff5d364d348d88d40
1 /*
2 * gEDA - GNU Electronic Design Automation
3 * drill.c
4 * Copyright (C) 2000-2006 Andreas Andersson
6 * $Id$
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 /** \file drill.c
24 \brief Excellon drill parsing functions
25 \ingroup libgerbv
29 * 21 Feb 2007 patch for metric drill files:
30 * 1) METRIC/INCH commands (partly) parsed to define units of the header
31 * 2) units of the header and the program body are independent
32 * 3) ICI command parsed in the header
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
39 #include <stdlib.h>
40 #include <glib.h>
41 #include <locale.h>
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
47 #include <math.h> /* pow() */
48 #include <ctype.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
57 #include "attribute.h"
58 #include "common.h"
59 #include "gerbv.h"
60 #include "drill.h"
61 #include "drill_stats.h"
63 #include "common.h"
65 /* DEBUG printing. #define DEBUG 1 in config.h to use this fcn. */
66 #define dprintf if(DEBUG) printf
68 #define NOT_IMPL(fd, s) do { \
69 GERB_MESSAGE("Not Implemented:%s\n", s); \
70 } while(0)
72 #define MAXL 200
73 #define DRILL_READ_DOUBLE_SIZE 32
75 #undef max
76 #define max(a,b) ((a) > (b) ? (a) : (b))
77 #undef min
78 #define min(a,b) ((a) < (b) ? (a) : (b))
80 enum drill_file_section_t {DRILL_NONE, DRILL_HEADER, DRILL_DATA};
81 enum drill_coordinate_mode_t {DRILL_MODE_ABSOLUTE, DRILL_MODE_INCREMENTAL};
83 enum drill_m_code_t {DRILL_M_UNKNOWN, DRILL_M_NOT_IMPLEMENTED,
84 DRILL_M_END, DRILL_M_ENDREWIND,
85 DRILL_M_MESSAGE, DRILL_M_LONGMESSAGE,
86 DRILL_M_HEADER, DRILL_M_ENDHEADER,
87 DRILL_M_METRIC, DRILL_M_IMPERIAL,
88 DRILL_M_BEGINPATTERN, DRILL_M_ENDPATTERN,
89 DRILL_M_CANNEDTEXT, DRILL_M_TIPCHECK,
90 DRILL_M_METRICHEADER, DRILL_M_IMPERIALHEADER};
93 enum drill_g_code_t {DRILL_G_ABSOLUTE, DRILL_G_INCREMENTAL,
94 DRILL_G_ZEROSET, DRILL_G_UNKNOWN,
95 DRILL_G_ROUT, DRILL_G_DRILL,
96 DRILL_G_LINEARMOVE, DRILL_G_CWMOVE, DRILL_G_CCWMOVE};
98 enum number_fmt_t {FMT_00_0000 /* INCH */,
99 FMT_000_000 /* METRIC 6-digit, 1 um */,
100 FMT_000_00 /* METRIC 5-digit, 10 um */,
101 FMT_0000_00 /* METRIC 6-digit, 10 um */,
102 FMT_USER /* User defined format */};
104 typedef struct drill_state {
105 double curr_x;
106 double curr_y;
107 int current_tool;
108 int curr_section;
109 int coordinate_mode;
110 double origin_x;
111 double origin_y;
112 gerbv_unit_t unit;
113 /* number_format is used throughout the file itself.
115 header_number_format is used to parse the tool definition C
116 codes within the header. It is fixed to FMT_00_0000 for INCH
117 measures, and FMT_000_000 (1 um resolution) for metric
118 measures. */
119 enum number_fmt_t number_format, header_number_format;
120 /* Used as a backup when temporarily switching to INCH. */
121 enum number_fmt_t backup_number_format;
123 /* 0 means we don't try to autodetect any of the other values */
124 int autod;
126 /* in FMT_USER this specifies the number of digits before the
127 * decimal point when doing trailing zero supression. Otherwise
128 * it is the number of digits *after* the decimal
129 * place in the file
131 int decimals;
133 } drill_state_t;
135 /* Local function prototypes */
136 static int drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image);
137 static int drill_parse_M_code(gerb_file_t *fd, drill_state_t *state,
138 gerbv_image_t *image);
139 static int drill_parse_T_code(gerb_file_t *fd, drill_state_t *state,
140 gerbv_image_t *image);
141 static void drill_parse_coordinate(gerb_file_t *fd, char firstchar,
142 gerbv_image_t *image, drill_state_t *state);
143 static drill_state_t *new_state(drill_state_t *state);
144 static double read_double(gerb_file_t *fd, enum number_fmt_t fmt,
145 gerbv_omit_zeros_t omit_zeros, int decimals);
146 static void eat_line(gerb_file_t *fd);
147 static char *get_line(gerb_file_t *fd);
149 /* -------------------------------------------------------------- */
150 /* This is the list of specific attributes a drill file may have from
151 * the point of view of parsing it.
154 static const char *supression_list[] = {
155 "None",
156 #define SUP_NONE 0
157 "Leading",
158 #define SUP_LEAD 1
159 "Trailing",
160 #define SUP_TRAIL 2
164 static const char *units_list[] = {
165 "inch",
166 #define UNITS_INCH 0
167 /* we don't do anything with mil yet so don't offer it as an
168 option
169 "mil (1/1000 inch)",
170 #define UNITS_MIL 1
172 "mm",
173 #define UNITS_MM 1
177 static gerbv_HID_Attribute drill_attribute_list[] = {
178 /* This should be first */
179 {"autodetect", "Try to autodetect the file format",
180 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
181 #define HA_auto 0
183 {"zero_supression", "Zero supression",
184 HID_Enum, 0, 0, {0, 0, 0}, supression_list, 0},
185 #define HA_supression 1
187 {"units", "Units",
188 HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
189 #define HA_xy_units 2
191 #if 0
192 {"tool_units", "Tool size units",
193 HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
194 #define HA_tool_units 3
195 #endif
197 {"digits", "Number of digits. For trailing zero supression,"
198 " this is the number of digits before the decimal point. "
199 "Otherwise this is the number of digits after the decimal point.",
200 HID_Integer, 0, 20, {5, 0, 0}, 0, 0},
201 #define HA_digits 3
205 void
206 drill_attribute_merge (gerbv_HID_Attribute *dest, int ndest, gerbv_HID_Attribute *src, int nsrc)
208 int i, j;
210 /* Here is a brain dead merge algorithm which shold make anyone cringe.
211 * Still, it is simple and we won't merge many attributes and not
212 * many times either.
215 for (i = 0 ; i < nsrc ; i++) {
216 /* see if our destination wants this attribute */
217 j = 0;
218 while (j < ndest && strcmp (src[i].name, dest[j].name) != 0)
219 j++;
221 /* if we wanted it and it is the same type, copy it over */
222 if (j < ndest && src[i].type == dest[j].type) {
223 dest[j].default_val = src[i].default_val;
230 * Adds the actual drill hole to the drawing
232 static gerbv_net_t *
233 drill_add_drill_hole (gerbv_image_t *image, drill_state_t *state, gerbv_drill_stats_t *stats, gerbv_net_t *curr_net)
235 /* Add one to drill stats for the current tool */
236 drill_stats_increment_drill_counter(image->drill_stats->drill_list,
237 state->current_tool);
239 curr_net->next = (gerbv_net_t *)g_malloc(sizeof(gerbv_net_t));
240 if (curr_net->next == NULL)
241 GERB_FATAL_ERROR("malloc curr_net->next failed\n");
242 curr_net = curr_net->next;
243 memset((void *)curr_net, 0, sizeof(gerbv_net_t));
244 curr_net->layer = image->layers;
245 curr_net->state = image->states;
246 curr_net->start_x = (double)state->curr_x;
247 curr_net->start_y = (double)state->curr_y;
248 /* KLUDGE. This function isn't allowed to return anything
249 but inches */
250 if(state->unit == GERBV_UNIT_MM) {
251 curr_net->start_x /= 25.4;
252 curr_net->start_y /= 25.4;
253 /* KLUDGE. All images, regardless of input format,
254 are returned in INCH format */
255 curr_net->state->unit = GERBV_UNIT_INCH;
258 curr_net->stop_x = curr_net->start_x - state->origin_x;
259 curr_net->stop_y = curr_net->start_y - state->origin_y;
260 curr_net->aperture = state->current_tool;
261 curr_net->aperture_state = GERBV_APERTURE_STATE_FLASH;
263 /* Find min and max of image.
264 Mustn't forget (again) to add the hole radius */
266 /* Check if aperture is set. Ignore the below instead of
267 causing SEGV... */
268 if(image->aperture[state->current_tool] == NULL)
269 return curr_net;
271 curr_net->boundingBox.left=curr_net->start_x -
272 image->aperture[state->current_tool]->parameter[0] / 2;
273 curr_net->boundingBox.right=curr_net->start_x +
274 image->aperture[state->current_tool]->parameter[0] / 2;
275 curr_net->boundingBox.bottom=curr_net->start_y -
276 image->aperture[state->current_tool]->parameter[0] / 2;
277 curr_net->boundingBox.top=curr_net->start_y +
278 image->aperture[state->current_tool]->parameter[0] / 2;
280 image->info->min_x =
281 min(image->info->min_x,
282 (curr_net->start_x -
283 image->aperture[state->current_tool]->parameter[0] / 2));
284 image->info->min_y =
285 min(image->info->min_y,
286 (curr_net->start_y -
287 image->aperture[state->current_tool]->parameter[0] / 2));
288 image->info->max_x =
289 max(image->info->max_x,
290 (curr_net->start_x +
291 image->aperture[state->current_tool]->parameter[0] / 2));
292 image->info->max_y =
293 max(image->info->max_y,
294 (curr_net->start_y +
295 image->aperture[state->current_tool]->parameter[0] / 2));
297 return curr_net;
300 /* -------------------------------------------------------------- */
301 gerbv_image_t *
302 parse_drillfile(gerb_file_t *fd, gerbv_HID_Attribute *attr_list, int n_attr, int reload)
304 drill_state_t *state = NULL;
305 gerbv_image_t *image = NULL;
306 gerbv_net_t *curr_net = NULL;
307 int read;
308 gerbv_drill_stats_t *stats;
309 gchar *tmps;
310 gchar *string;
313 * many locales redefine "." as "," and so on, so sscanf and strtod
314 * has problems when reading files using %f format.
315 * Fixes bug #1963618 reported by Lorenzo Marcantonio.
317 setlocale(LC_NUMERIC, "C" );
319 /* Create new image for this layer */
320 dprintf("In parse_drillfile, about to create image for this layer\n");
322 image = gerbv_create_image(image, "Excellon Drill File");
323 if (image == NULL)
324 GERB_FATAL_ERROR("malloc image failed\n");
326 if (reload && attr_list != NULL) {
327 /* FIXME there should probably just be a function to copy an
328 attribute list including using strdup as needed */
330 image->info->n_attr = n_attr;
331 image->info->attr_list = gerbv_attribute_dup(attr_list, n_attr);
333 } else {
334 /* Copy in the default attribute list for drill files. We make a
335 * copy here because we will allow per-layer editing of the
336 * attributes.
338 image->info->n_attr = sizeof (drill_attribute_list) / sizeof (drill_attribute_list[0]);
339 image->info->attr_list = gerbv_attribute_dup (drill_attribute_list, image->info->n_attr);
341 /* now merge any project attributes */
342 drill_attribute_merge (image->info->attr_list, image->info->n_attr,
343 attr_list, n_attr);
346 curr_net = image->netlist;
347 curr_net->layer = image->layers;
348 curr_net->state = image->states;
349 image->layertype = GERBV_LAYERTYPE_DRILL;
350 stats = gerbv_drill_stats_new();
351 if (stats == NULL)
352 GERB_FATAL_ERROR("malloc stats failed\n");
353 image->drill_stats = stats;
355 /* Create local state variable to track photoplotter state */
356 state = new_state(state);
357 if (state == NULL)
358 GERB_FATAL_ERROR("malloc state failed\n");
360 image->format = (gerbv_format_t *)g_malloc(sizeof(gerbv_format_t));
361 if (image->format == NULL)
362 GERB_FATAL_ERROR("malloc format failed\n");
363 memset((void *)image->format, 0, sizeof(gerbv_format_t));
364 image->format->omit_zeros = GERBV_OMIT_ZEROS_UNSPECIFIED;
367 if (!image->info->attr_list[HA_auto].default_val.int_value) {
368 state->autod = 0;
369 state->number_format = FMT_USER;
370 state->decimals = image->info->attr_list[HA_digits].default_val.int_value;
371 if (image->info->attr_list[HA_xy_units].default_val.int_value == UNITS_MM)
372 state->unit = GERBV_UNIT_MM;
373 switch (image->info->attr_list[HA_supression].default_val.int_value) {
374 case SUP_LEAD:
375 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
376 break;
378 case SUP_TRAIL:
379 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
380 break;
382 default:
383 image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
384 break;
388 dprintf("%s(): Starting parsing of drill file\n", __FUNCTION__);
389 while ((read = gerb_fgetc(fd)) != EOF) {
391 switch ((char) read) {
392 case ';' :
393 /* Comment found. Eat rest of line */
394 eat_line(fd);
395 break;
396 case 'D' :
397 gerb_ungetc (fd);
398 tmps = get_line (fd);
399 if (strcmp (tmps, "DETECT,ON") == 0 ||
400 strcmp (tmps, "DETECT,OFF") == 0) {
401 gchar *tmps2;
402 gchar *tmps3;
403 if (strcmp (tmps, "DETECT,ON") == 0)
404 tmps3 = "ON";
405 else
406 tmps3 = "OFF";
408 /* broken tool detect on/off. Silently ignored. */
409 if (stats->detect) {
410 tmps2 = g_strdup_printf ("%s\n%s", stats->detect, tmps3);
411 g_free (stats->detect);
412 } else {
413 tmps2 = g_strdup_printf ("%s", tmps3);
415 stats->detect = tmps2;
416 } else {
417 string = g_strdup_printf("Undefined header line = '%s'\n",tmps);
418 drill_stats_add_error(stats->error_list,
420 string,
421 GERBV_MESSAGE_NOTE);
422 g_free(string);
424 g_free (tmps);
425 break;
426 case 'F' :
427 gerb_ungetc (fd);
428 tmps = get_line (fd);
429 /* Silently ignore FMAT,2. Not sure what others are allowed */
430 if (strcmp (tmps, "FMAT,2") != 0) {
431 string = g_strdup_printf("Undefined header line = '%s'\n",tmps);
432 drill_stats_add_error(stats->error_list,
434 string,
435 GERBV_MESSAGE_NOTE);
436 g_free(string);
438 g_free (tmps);
439 break;
441 case 'G':
442 /* Most G codes aren't used, for now */
443 switch(drill_parse_G_code(fd, image)) {
444 case DRILL_G_ROUT :
445 drill_stats_add_error(stats->error_list,
447 "Rout mode data is not supported\n",
448 GERBV_MESSAGE_ERROR);
449 break;
450 case DRILL_G_DRILL :
451 break;
452 case DRILL_G_ABSOLUTE :
453 state->coordinate_mode = DRILL_MODE_ABSOLUTE;
454 break;
455 case DRILL_G_INCREMENTAL :
456 state->coordinate_mode = DRILL_MODE_INCREMENTAL;
457 break;
458 case DRILL_G_ZEROSET :
459 if((read = gerb_fgetc(fd)) == EOF)
460 drill_stats_add_error(stats->error_list,
462 "Unexpected EOF found.\n",
463 GERBV_MESSAGE_ERROR);
464 drill_parse_coordinate(fd, (char)read, image, state);
465 state->origin_x = state->curr_x;
466 state->origin_y = state->curr_y;
467 break;
468 default :
469 eat_line(fd);
470 break;
472 break;
473 case 'I':
474 if (state->curr_section != DRILL_HEADER)
475 break;
477 int c = gerb_fgetc(fd);
478 switch (c) {
479 case 'N':
480 if ('C' == gerb_fgetc(fd)) {
481 if ('H' == gerb_fgetc(fd)) {
482 state->unit = GERBV_UNIT_INCH;
484 /* Look for TZ/LZ */
485 if (',' == gerb_fgetc(fd)) {
486 c = gerb_fgetc(fd);
487 if (c != EOF && 'Z' == gerb_fgetc(fd)) {
488 switch (c) {
489 case 'L':
490 if (state->autod) {
491 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
492 state->header_number_format =
493 state->number_format = FMT_00_0000;
494 state->decimals = 4;
496 break;
498 case 'T':
499 if (state->autod) {
500 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
501 state->header_number_format =
502 state->number_format = FMT_00_0000;
503 state->decimals = 4;
505 break;
507 default:
508 drill_stats_add_error(stats->error_list,
510 "Found junk after INCH command\n",
511 GERBV_MESSAGE_WARNING);
512 break;
514 } else {
515 drill_stats_add_error(stats->error_list,
517 "Found junk after INCH command\n",
518 GERBV_MESSAGE_WARNING);
521 else
522 /* unget the char in case we just advanced past a new line char */
523 gerb_ungetc (fd);
526 break;
527 case 'C':
528 if ('I' == gerb_fgetc(fd))
529 if (',' == gerb_fgetc(fd))
530 if ('O' == gerb_fgetc(fd)) {
531 if ('N' == (c = gerb_fgetc(fd)))
532 state->coordinate_mode = DRILL_MODE_INCREMENTAL;
533 else if ('F' == c) if ('F' == gerb_fgetc(fd))
534 state->coordinate_mode = DRILL_MODE_ABSOLUTE;
536 break;
538 eat_line(fd);
540 break;
542 case 'M':
543 switch(drill_parse_M_code(fd, state, image)) {
544 case DRILL_M_HEADER :
545 state->curr_section = DRILL_HEADER;
546 break;
547 case DRILL_M_ENDHEADER :
548 state->curr_section = DRILL_DATA;
550 if (image->format->omit_zeros == GERBV_OMIT_ZEROS_UNSPECIFIED) {
551 /* Excellon says they default to specify leading
552 zeros, i.e. omit trailing zeros. The Excellon
553 files floating around that don't specify the
554 leading/trailing zeros in the header seem to
555 contradict to this though.
557 XXX We should probably ask the user. */
559 drill_stats_add_error(stats->error_list,
561 "End of Excellon header reached but no leading/trailing zero handling specified.\n",
562 GERBV_MESSAGE_ERROR);
563 drill_stats_add_error(stats->error_list,
565 "Assuming leading zeros.\n",
566 GERBV_MESSAGE_WARNING);
567 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
569 break;
570 case DRILL_M_METRIC :
571 if (state->unit == GERBV_UNIT_UNSPECIFIED &&
572 state->curr_section != DRILL_HEADER) {
573 drill_stats_add_error(stats->error_list,
575 "M71 code found but no METRIC specification in header.\n",
576 GERBV_MESSAGE_ERROR);
577 drill_stats_add_error(stats->error_list,
579 "Assuming all tool sizes are MM.\n",
580 GERBV_MESSAGE_WARNING);
581 int tool_num;
582 double size;
583 stats = image->drill_stats;
584 for (tool_num = TOOL_MIN; tool_num < TOOL_MAX; tool_num++) {
585 if (image->aperture && image->aperture[tool_num]) {
586 /* First update stats. Do this before changing drill dias.
587 * Maybe also put error into stats? */
588 size = image->aperture[tool_num]->parameter[0];
589 drill_stats_modify_drill_list(stats->drill_list,
590 tool_num,
591 size,
592 "MM");
593 /* Now go back and update all tool dias, since
594 * tools are displayed in inch units
596 image->aperture[tool_num]->parameter[0] /= 25.4;
600 if (state->autod) {
601 state->number_format = state->backup_number_format;
602 state->unit = GERBV_UNIT_MM;
604 break;
605 case DRILL_M_IMPERIAL :
606 if (state->autod) {
607 if (state->number_format != FMT_00_0000)
608 /* save metric format definition for later */
609 state->backup_number_format = state->number_format;
610 state->number_format = FMT_00_0000;
611 state->decimals = 4;
612 state->unit = GERBV_UNIT_INCH;
615 break;
616 case DRILL_M_LONGMESSAGE :
617 case DRILL_M_MESSAGE :
618 case DRILL_M_CANNEDTEXT :
619 tmps = get_line(fd);
620 string = g_strdup_printf("Message embedded in drill file: '%s'\n",
621 tmps);
622 drill_stats_add_error(stats->error_list,
624 string,
625 GERBV_MESSAGE_NOTE);
626 g_free(tmps);
627 break;
628 case DRILL_M_NOT_IMPLEMENTED :
629 case DRILL_M_ENDPATTERN :
630 case DRILL_M_TIPCHECK :
631 break;
632 case DRILL_M_END :
633 /* M00 has optional arguments */
634 eat_line(fd);
635 case DRILL_M_ENDREWIND :
636 goto drill_parse_end;
637 break;
638 case DRILL_M_METRICHEADER :
639 state->unit = GERBV_UNIT_MM;
640 break;
641 default:
642 drill_stats_add_error(stats->error_list,
644 "Undefined M code found.\n",
645 GERBV_MESSAGE_ERROR);
647 break;
649 case 'R':
650 if(state->curr_section == DRILL_HEADER) {
651 drill_stats_add_error(stats->error_list,
653 "R codes are not allowed in the header.\n",
654 GERBV_MESSAGE_ERROR);
655 } else {
656 double start_x, start_y;
657 double step_x, step_y;
658 int c;
659 int rcnt;
661 * This is the "Repeat hole" command. Format is:
662 * R##[X##][Y##]
663 * This repeats the previous hole stepping in the X and
664 * Y increments give. Example:
665 * R04X0.1 -- repeats the drill hole 4 times, stepping
666 * 0.1" in the X direction. Note that the X and Y step
667 * sizes default to zero if not given and that they use
668 * the same format and units as the normal X,Y
669 * coordinates.
671 stats->R++;
673 start_x = state->curr_x;
674 start_y = state->curr_y;
678 /* figure out how many repeats there are */
679 c = gerb_fgetc (fd);
680 rcnt = 0;
681 while ( '0' <= c && c <= '9') {
682 rcnt = 10*rcnt + (c - '0');
683 c = gerb_fgetc (fd);
685 dprintf ("working on R code (repeat) with a number of reps equal to %d\n", rcnt);
687 step_x = 0.0;
688 if (c == 'X') {
689 step_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
690 c = gerb_fgetc (fd);
693 step_y = 0.0;
694 if( c == 'Y') {
695 step_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
696 } else {
697 gerb_ungetc (fd);
700 dprintf ("Getting ready to repeat the drill %d times with delta_x = %g, delta_y = %g\n", rcnt, step_x, step_y);
702 /* spit out the drills */
703 for (c = 1 ; c <= rcnt ; c++) {
704 state->curr_x = start_x + c*step_x;
705 state->curr_y = start_y + c*step_y;
706 dprintf (" Repeat #%d -- new location is (%g, %g)\n", c, state->curr_x, state->curr_y);
707 curr_net = drill_add_drill_hole (image, state, stats, curr_net);
712 case 'S':
713 drill_stats_add_error(stats->error_list,
715 "Drill file sets spindle speed -- ignoring.\n",
716 GERBV_MESSAGE_NOTE);
717 eat_line(fd);
718 break;
719 case 'T':
720 drill_parse_T_code(fd, state, image);
721 break;
722 case 'V' :
723 gerb_ungetc (fd);
724 tmps = get_line (fd);
725 /* Silently ignore VER,1. Not sure what others are allowed */
726 if (strcmp (tmps, "VER,1") != 0) {
727 string = g_strdup_printf("Undefined header line = '%s'\n",tmps);
728 drill_stats_add_error(stats->error_list,
730 g_strdup_printf("Undefined header line = '%s'\n",tmps),
731 GERBV_MESSAGE_NOTE);
732 g_free(string);
734 g_free (tmps);
735 break;
737 case 'X':
738 case 'Y':
739 /* Hole coordinate found. Do some parsing */
740 drill_parse_coordinate(fd, read, image, state);
742 /* add the new drill hole */
743 curr_net = drill_add_drill_hole (image, state, stats, curr_net);
744 break;
746 case '%':
747 state->curr_section = DRILL_DATA;
748 break;
749 case 10 : /* White space */
750 case 13 :
751 case ' ' :
752 case '\t' :
753 break;
754 default:
755 if(state->curr_section == DRILL_HEADER) {
756 /* Unrecognised crap in the header is thrown away */
757 drill_stats_add_error(stats->error_list,
759 "Undefined codes found in header.\n",
760 GERBV_MESSAGE_ERROR);
761 gerb_ungetc(fd);
762 tmps = get_line(fd);
763 string = g_strdup_printf("Undefined header line = '%s'\n",
764 tmps);
765 drill_stats_add_error(stats->error_list,
767 string,
768 GERBV_MESSAGE_NOTE);
769 g_free(string);
770 g_free (tmps);
771 } else {
772 string = g_strdup_printf("Undefined character '%c' [0x%02x] found inside data, ignoring\n",
773 read, read);
774 drill_stats_add_error(stats->error_list,
776 string,
777 GERBV_MESSAGE_ERROR);
778 g_free(string);
782 drill_stats_add_error(stats->error_list,
784 "No EOF found in drill file.\n",
785 GERBV_MESSAGE_ERROR);
787 drill_parse_end:
788 dprintf ("%s(): Populating file attributes\n", __FUNCTION__);
790 switch (state->unit) {
791 case GERBV_UNIT_MM:
792 image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_MM;
793 break;
795 default:
796 image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_INCH;
797 break;
800 switch (state->number_format) {
801 case FMT_000_00:
802 case FMT_0000_00:
803 image->info->attr_list[HA_digits].default_val.int_value = 2;
804 break;
806 case FMT_000_000:
807 image->info->attr_list[HA_digits].default_val.int_value = 3;
808 break;
810 case FMT_00_0000:
811 image->info->attr_list[HA_digits].default_val.int_value = 4;
812 break;
814 case FMT_USER:
815 dprintf ("%s(): Keeping user specified number of decimal places (%d)\n",
816 __FUNCTION__,
817 image->info->attr_list[HA_digits].default_val.int_value);
818 break;
820 default:
821 break;
824 switch (image->format->omit_zeros) {
825 case GERBV_OMIT_ZEROS_LEADING:
826 image->info->attr_list[HA_supression].default_val.int_value = SUP_LEAD;
827 break;
829 case GERBV_OMIT_ZEROS_TRAILING:
830 image->info->attr_list[HA_supression].default_val.int_value = SUP_TRAIL;
831 break;
833 default:
834 image->info->attr_list[HA_supression].default_val.int_value = SUP_NONE;
835 break;
838 g_free(state);
840 return image;
841 } /* parse_drillfile */
844 /* -------------------------------------------------------------- */
846 * Checks for signs that this is a drill file
847 * Returns TRUE if it is, FALSE if not.
849 gboolean
850 drill_file_p(gerb_file_t *fd, gboolean *returnFoundBinary)
852 char *buf = NULL, *tbuf = NULL;
853 int len = 0;
854 int idx = 0;
855 char *letter;
856 int ascii;
857 int zero = 48; /* ascii 0 */
858 int nine = 57; /* ascii 9 */
859 int i;
860 gboolean found_binary = FALSE;
861 gboolean found_M48 = FALSE;
862 gboolean found_M30 = FALSE;
863 gboolean found_percent = FALSE;
864 gboolean found_T = FALSE;
865 gboolean found_X = FALSE;
866 gboolean found_Y = FALSE;
867 gboolean end_comments = FALSE;
869 dprintf ("drill_file_p(%p, %p), fd->fd = %p, fd->zfd = %p %s\n",
870 fd, returnFoundBinary, fd->fd, fd->zfd, fd->filename);
872 while (1) {
874 /* Release the buffer if it was in use previously */
875 if (tbuf) {
876 g_free(tbuf);
877 tbuf = NULL;
880 /* Read one line at the time using gerb_file operations to support zip*/
881 tbuf = gerb_fgetstring(fd, '\n');
883 /* EOF detected */
884 if (tbuf == NULL) {
885 break;
888 /* Read the \n */
889 gerb_fgetc(fd);
891 len = strlen(tbuf);
892 if (len == 0) {
893 continue;
896 buf = tbuf;
898 dprintf ("buf = \"%s\"\n", buf);
900 /* Check for comments at top of file. */
901 if ((!end_comments) &&
902 (g_strstr_len(buf, len, ";") != NULL)) {
903 for (i = 0; i < len - 1 ; ++i){
904 if ((buf[i] == '\n') &&
905 (buf[i+1] != ';') &&
906 (buf[i+1] != '\r') &&
907 (buf[i+1] != '\n')) {
908 end_comments = TRUE;
909 buf = &tbuf[i+1];/* set rest of parser to end of comments */
910 if (!end_comments) {
911 continue;
913 } else {
914 end_comments = TRUE;
919 /* First look through the file for indications of its type */
920 len = strlen(buf);
921 /* check that file is not binary (non-printing chars) */
922 for (i = 0; i < len; i++) {
923 ascii = (int) buf[i];
924 if ((ascii > 128) || (ascii < 0)) {
925 found_binary = TRUE;
929 /* Check for M48 = start of drill header */
930 if (g_strstr_len(buf, len, "M48")) {
931 found_M48 = TRUE;
934 /* Check for M30 = end of drill program */
935 if (g_strstr_len(buf, len, "M30")) {
936 if (found_percent) {
937 found_M30 = TRUE; /* Found M30 after % = good */
941 /* Check for % on its own line at end of header */
942 if ((letter = g_strstr_len(buf, len, "%")) != NULL) {
943 if ((letter[1] == '\r') || (letter[1] == '\n'))
944 found_percent = TRUE;
947 /* Check for T<number> */
948 if ((letter = g_strstr_len(buf, len, "T")) != NULL) {
949 if (!found_T && (found_X || found_Y)) {
950 found_T = FALSE; /* Found first T after X or Y */
951 } else {
952 if (isdigit( (int) letter[1])) { /* verify next char is digit */
953 found_T = TRUE;
958 /* look for X<number> or Y<number> */
959 if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
960 ascii = (int) letter[1]; /* grab char after X */
961 if ((ascii >= zero) && (ascii <= nine)) {
962 found_X = TRUE;
965 if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
966 ascii = (int) letter[1]; /* grab char after Y */
967 if ((ascii >= zero) && (ascii <= nine)) {
968 found_Y = TRUE;
973 g_free(tbuf);
975 *returnFoundBinary = found_binary;
977 gerb_freset(fd);
979 /* Now form logical expression determining if this is a drill file */
980 if ( ((found_X || found_Y) && found_T) &&
981 (found_M48 || (found_percent && found_M30)) ) {
982 return TRUE;
983 } else if (found_M48 && found_T && found_percent && found_M30) {
984 /* Pathological case of drill file with valid header
985 and EOF but no drill XY locations. */
986 return TRUE;
987 } else {
988 return FALSE;
990 } /* drill_file_p */
993 /* -------------------------------------------------------------- */
994 /* Parse tool definition. This can get a bit tricky since it can
995 appear in the header and/or data section.
996 Returns tool number on success, -1 on error */
997 static int
998 drill_parse_T_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
1000 int tool_num;
1001 gboolean done = FALSE;
1002 int temp;
1003 double size;
1004 gerbv_drill_stats_t *stats = image->drill_stats;
1005 gchar *tmps;
1006 gchar *string;
1008 /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
1009 broken headers from Orcad, which is crap */
1010 temp = gerb_fgetc(fd);
1011 dprintf("Found a char %d after the T\n", temp);
1013 /* might be a tool tool change stop switch on/off*/
1014 if((temp == 'C') && ((fd->ptr + 2) < fd->datalen)){
1015 if(gerb_fgetc(fd) == 'S'){
1016 if (gerb_fgetc(fd) == 'T' ){
1017 fd->ptr -= 4;
1018 tmps = get_line(fd++);
1019 string = g_strdup_printf("Tool change stop switch found: %s\n", tmps);
1020 drill_stats_add_error(stats->error_list,
1022 string,
1023 GERBV_MESSAGE_NOTE);
1024 g_free(string);
1025 g_free (tmps);
1026 return -1;
1028 gerb_ungetc(fd);
1030 gerb_ungetc(fd);
1033 if( !(isdigit(temp) != 0 || temp == '+' || temp =='-') ) {
1034 if(temp != EOF) {
1035 drill_stats_add_error(stats->error_list,
1037 "Orcad bug: Junk text found in place of tool definition.\n",
1038 GERBV_MESSAGE_ERROR);
1039 tmps = get_line(fd);
1040 string = g_strdup_printf("Junk text = %s\n",
1041 tmps);
1042 drill_stats_add_error(stats->error_list,
1044 string,
1045 GERBV_MESSAGE_NOTE);
1046 g_free(string);
1047 g_free (tmps);
1048 drill_stats_add_error(stats->error_list,
1050 "Ignorning junk text.\n",
1051 GERBV_MESSAGE_WARNING);
1053 return -1;
1055 gerb_ungetc(fd);
1057 tool_num = (int) gerb_fgetint(fd, NULL);
1058 dprintf ("In %s: handling tool_num = %d\n", __FUNCTION__, tool_num);
1060 if (tool_num == 0)
1061 return tool_num; /* T00 is a command to unload the drill */
1063 if ( (tool_num < TOOL_MIN) || (tool_num >= TOOL_MAX) ) {
1064 string = g_strdup_printf("Drill number out of bounds: %d.\n", tool_num);
1065 drill_stats_add_error(stats->error_list,
1067 string,
1068 GERBV_MESSAGE_ERROR);
1069 g_free(string);
1072 /* Set the current tool to the correct one */
1073 state->current_tool = tool_num;
1075 /* Check for a size definition */
1076 temp = gerb_fgetc(fd);
1078 /* This bit of code looks for a tool definition by scanning for strings
1079 * of form TxxC, TxxF, TxxS. */
1080 while(!done) {
1082 switch((char)temp) {
1083 case 'C':
1084 size = read_double(fd, state->header_number_format, GERBV_OMIT_ZEROS_TRAILING, state->decimals);
1085 dprintf ("%s: Read a size of %g %s\n", __FUNCTION__, size,
1086 state->unit == GERBV_UNIT_MM ? "mm" : "inch");
1087 if(state->unit == GERBV_UNIT_MM) {
1088 size /= 25.4;
1089 } else if(size >= 4.0) {
1090 /* If the drill size is >= 4 inches, assume that this
1091 must be wrong and that the units are mils.
1092 The limit being 4 inches is because the smallest drill
1093 I've ever seen used is 0,3mm(about 12mil). Half of that
1094 seemed a bit too small a margin, so a third it is */
1095 string = g_strdup_printf("Read a drill of diameter %g inches.\n", size);
1096 drill_stats_add_error(stats->error_list,
1098 string,
1099 GERBV_MESSAGE_ERROR);
1100 g_free(string);
1101 string = g_strdup_printf("Assuming units are mils.\n");
1102 drill_stats_add_error(stats->error_list,
1104 string,
1105 GERBV_MESSAGE_WARNING);
1106 g_free(string);
1107 size /= 1000.0;
1110 if(size <= 0. || size >= 10000.) {
1111 string = g_strdup_printf("Unreasonable drill size found for drill %d: %g\n", tool_num, size);
1112 drill_stats_add_error(stats->error_list,
1114 string,
1115 GERBV_MESSAGE_ERROR);
1116 g_free(string);
1117 } else {
1118 if(image->aperture[tool_num] != NULL) {
1119 /* allow a redefine of a tool only if the new definition is exactly the same.
1120 * This avoid lots of spurious complaints with the output of some cad
1121 * tools while keeping complaints if there is a true problem
1123 if (image->aperture[tool_num]->parameter[0] != size ||
1124 image->aperture[tool_num]->type != GERBV_APTYPE_CIRCLE ||
1125 image->aperture[tool_num]->nuf_parameters != 1 ||
1126 image->aperture[tool_num]->unit != GERBV_UNIT_INCH) {
1127 string = g_strdup_printf("Found redefinition of drill %d.\n", tool_num);
1128 drill_stats_add_error(stats->error_list,
1130 string,
1131 GERBV_MESSAGE_ERROR);
1132 g_free(string);
1134 } else {
1135 image->aperture[tool_num] =
1136 (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
1137 if (image->aperture[tool_num] == NULL) {
1138 GERB_FATAL_ERROR("malloc tool failed\n");
1140 /* make sure we zero out all aperature parameters */
1141 memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
1142 /* There's really no way of knowing what unit the tools
1143 are defined in without sneaking a peek in the rest of
1144 the file first. That's done in drill_guess_format() */
1145 image->aperture[tool_num]->parameter[0] = size;
1146 image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1147 image->aperture[tool_num]->nuf_parameters = 1;
1148 image->aperture[tool_num]->unit = GERBV_UNIT_INCH;
1152 /* Add the tool whose definition we just found into the list
1153 * of tools for this layer used to generate statistics. */
1154 stats = image->drill_stats;
1155 string = g_strdup_printf("%s", (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
1156 drill_stats_add_to_drill_list(stats->drill_list,
1157 tool_num,
1158 state->unit == GERBV_UNIT_MM ? size*25.4 : size,
1159 string);
1160 g_free(string);
1161 break;
1163 case 'F':
1164 case 'S' :
1165 /* Silently ignored. They're not important. */
1166 gerb_fgetint(fd, NULL);
1167 break;
1169 default:
1170 /* Stop when finding anything but what's expected
1171 (and put it back) */
1172 gerb_ungetc(fd);
1173 done = TRUE;
1174 break;
1175 } /* switch((char)temp) */
1177 if( (temp = gerb_fgetc(fd)) == EOF) {
1178 drill_stats_add_error(stats->error_list,
1180 "Unexpected EOF encountered header of drill file.\n",
1181 GERBV_MESSAGE_ERROR);
1183 } /* while(!done) */ /* Done looking at tool definitions */
1185 /* Catch the tools that aren't defined.
1186 This isn't strictly a good thing, but at least something is shown */
1187 if(image->aperture[tool_num] == NULL) {
1188 double dia;
1190 image->aperture[tool_num] =
1191 (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
1192 if (image->aperture[tool_num] == NULL) {
1193 GERB_FATAL_ERROR("malloc tool failed\n");
1195 /* make sure we zero out all aperature parameters */
1196 memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
1198 /* See if we have the tool table */
1199 dia = gerbv_get_tool_diameter(tool_num);
1200 if (dia <= 0) {
1202 * There is no tool. So go out and make some.
1203 * This size calculation is, of course, totally bogus.
1205 dia = (double)(16 + 8 * tool_num) / 1000;
1207 * Oooh, this is sooo ugly. But some CAD systems seem to always
1208 * use T00 at the end of the file while others that don't have
1209 * tool definitions inside the file never seem to use T00 at all.
1211 if(tool_num != 0) {
1212 string = g_strdup_printf("Tool %02d used without being defined\n", tool_num);
1213 drill_stats_add_error(stats->error_list,
1215 string,
1216 GERBV_MESSAGE_ERROR);
1217 g_free(string);
1218 string = g_strdup_printf("Setting a default size of %g\"\n", dia);
1219 drill_stats_add_error(stats->error_list,
1221 string,
1222 GERBV_MESSAGE_WARNING);
1223 g_free(string);
1227 image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1228 image->aperture[tool_num]->nuf_parameters = 1;
1229 image->aperture[tool_num]->parameter[0] = dia;
1231 /* Add the tool whose definition we just found into the list
1232 * of tools for this layer used to generate statistics. */
1233 if (tool_num != 0) { /* Only add non-zero tool nums.
1234 * Zero = unload command. */
1235 stats = image->drill_stats;
1236 string = g_strdup_printf("%s",
1237 (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
1238 drill_stats_add_to_drill_list(stats->drill_list,
1239 tool_num,
1240 state->unit == GERBV_UNIT_MM ? dia*25.4 : dia,
1241 string);
1242 g_free(string);
1244 } /* if(image->aperture[tool_num] == NULL) */
1246 return tool_num;
1247 } /* drill_parse_T_code */
1250 /* -------------------------------------------------------------- */
1251 static int
1252 drill_parse_M_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
1254 char op[3] = " ";
1255 int read[3];
1256 gerbv_drill_stats_t *stats = image->drill_stats;
1257 int result=0;
1259 dprintf("---> entering drill_parse_M_code ...\n");
1261 read[0] = gerb_fgetc(fd);
1262 read[1] = gerb_fgetc(fd);
1264 if ((read[0] == EOF) || (read[1] == EOF))
1265 drill_stats_add_error(stats->error_list,
1267 "Unexpected EOF found while parsing M code.\n",
1268 GERBV_MESSAGE_ERROR);
1269 op[0] = read[0], op[1] = read[1], op[2] = 0;
1271 if (strncmp(op, "00", 2) == 0) {
1272 stats->M00++;
1273 result = DRILL_M_END;
1274 } else if (strncmp(op, "01", 2) == 0) {
1275 stats->M01++;
1276 result = DRILL_M_ENDPATTERN;
1277 } else if (strncmp(op, "18", 2) == 0) {
1278 stats->M18++;
1279 result = DRILL_M_TIPCHECK;
1280 } else if (strncmp(op, "25", 2) == 0) {
1281 stats->M25++;
1282 result = DRILL_M_BEGINPATTERN;
1283 } else if (strncmp(op, "31", 2) == 0) {
1284 stats->M31++;
1285 result = DRILL_M_BEGINPATTERN;
1286 } else if (strncmp(op, "30", 2) == 0) {
1287 stats->M30++;
1288 result = DRILL_M_ENDREWIND;
1289 } else if (strncmp(op, "45", 2) == 0) {
1290 stats->M45++;
1291 result = DRILL_M_LONGMESSAGE;
1292 } else if (strncmp(op, "47", 2) == 0) {
1293 stats->M47++;
1294 result = DRILL_M_MESSAGE;
1295 } else if (strncmp(op, "48", 2) == 0) {
1296 stats->M48++;
1297 result = DRILL_M_HEADER;
1298 } else if (strncmp(op, "71", 2) == 0) {
1299 eat_line(fd);
1300 stats->M71++;
1301 result = DRILL_M_METRIC;
1302 } else if (strncmp(op, "72", 2) == 0) {
1303 eat_line(fd);
1304 stats->M72++;
1305 result = DRILL_M_IMPERIAL;
1306 } else if (strncmp(op, "95", 2) == 0) {
1307 stats->M95++;
1308 result = DRILL_M_ENDHEADER;
1309 } else if (strncmp(op, "97", 2) == 0) {
1310 stats->M97++;
1311 result = DRILL_M_CANNEDTEXT;
1312 } else if (strncmp(op, "98", 2) == 0) {
1313 stats->M98++;
1314 return DRILL_M_CANNEDTEXT;
1315 } else if (state->curr_section == DRILL_HEADER &&
1316 strncmp(op, "ET", 2) == 0) {
1317 /* METRIC is not an actual M code but a command that is only
1318 acceptable within the header.
1320 The syntax is
1321 METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
1323 if ('R' == gerb_fgetc(fd) &&
1324 'I' == gerb_fgetc(fd) &&
1325 'C' == gerb_fgetc(fd)) {
1326 again:
1327 if (',' == gerb_fgetc(fd)) {
1328 int c;
1330 /* Is it tzlz, or zerofmt? */
1331 switch ((c = gerb_fgetc(fd))) {
1332 case 'T':
1333 case 'L':
1334 if ('Z' != gerb_fgetc(fd))
1335 goto junk;
1336 if (c == 'L')
1338 dprintf ("%s(): Detected a file that probably has trailing zero supression\n", __FUNCTION__);
1339 if (state->autod)
1341 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1344 else
1346 dprintf ("%s(): Detected a file that probably has leading zero supression\n", __FUNCTION__);
1347 if (state->autod)
1349 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1352 if (state->autod)
1354 /* Default metric number format is 6-digit, 1 um
1355 resolution. The header number format (for T#C#
1356 definitions) is fixed to that, while the number
1357 format within the file can differ. */
1358 state->header_number_format =
1359 state->number_format = FMT_000_000;
1360 state->decimals = 3;
1362 c = gerb_fgetc(fd);
1363 gerb_ungetc(fd);
1364 if (c == ',')
1365 /* anticipate number format will follow */
1366 goto again;
1367 break;
1369 case '0':
1370 if ('0' != gerb_fgetc(fd) ||
1371 '0' != gerb_fgetc(fd))
1372 goto junk;
1373 /* We just parsed three 0s, the remainder options
1374 so far are: .000 | .00 | 0.00 */
1375 read[0] = gerb_fgetc(fd);
1376 read[1] = gerb_fgetc(fd);
1377 if (read[0] == EOF || read[1] == EOF)
1378 goto junk;
1379 op[0] = read[0];
1380 op[1] = read[1];
1381 if (strcmp(op, "0.") == 0) {
1382 /* expecting FMT_0000_00,
1383 two trailing 0s must follow */
1384 if ('0' != gerb_fgetc(fd) ||
1385 '0' != gerb_fgetc(fd))
1386 goto junk;
1387 eat_line(fd);
1388 if (state->autod)
1390 state->number_format = FMT_0000_00;
1391 state->decimals = 2;
1393 break;
1395 if (strcmp(op, ".0") != 0)
1396 goto junk;
1397 /* must be either FMT_000_000 or FMT_000_00, depending
1398 on whether one or two 0s are following */
1399 if ('0' != gerb_fgetc(fd))
1400 goto junk;
1401 if ('0' == gerb_fgetc(fd) && state->autod)
1403 state->number_format = FMT_000_000;
1404 state->decimals = 3;
1406 else {
1407 gerb_ungetc(fd);
1408 if (state->autod)
1410 state->number_format = FMT_000_00;
1411 state->decimals = 2;
1414 eat_line(fd);
1415 break;
1417 default:
1418 junk:
1419 drill_stats_add_error(stats->error_list,
1421 "Found junk after METRIC command\n",
1422 GERBV_MESSAGE_WARNING);
1423 gerb_ungetc(fd);
1424 eat_line(fd);
1425 break;
1427 } else {
1428 gerb_ungetc(fd);
1429 eat_line(fd);
1432 return DRILL_M_METRICHEADER;
1434 } else {
1435 stats->M_unknown++;
1436 result = DRILL_M_UNKNOWN;
1439 dprintf("<---- ...leaving drill_parse_M_code.\n");
1440 return result;
1441 } /* drill_parse_M_code */
1444 /* -------------------------------------------------------------- */
1445 static int
1446 drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image)
1448 char op[3] = " ";
1449 int read[3];
1450 gerbv_drill_stats_t *stats = image->drill_stats;
1451 int result;
1453 dprintf("---> entering drill_parse_G_code ...\n");
1455 read[0] = gerb_fgetc(fd);
1456 read[1] = gerb_fgetc(fd);
1458 if ((read[0] == EOF) || (read[1] == EOF)) {
1459 drill_stats_add_error(stats->error_list,
1461 "Unexpected EOF found while parsing G code.\n",
1462 GERBV_MESSAGE_ERROR);
1465 op[0] = read[0], op[1] = read[1], op[2] = 0;
1467 if (strncmp(op, "00", 2) == 0) {
1468 stats->G00++;
1469 result = DRILL_G_ROUT;
1470 } else if (strncmp(op, "01", 2) == 0) {
1471 stats->G01++;
1472 result = DRILL_G_LINEARMOVE;
1473 } else if (strncmp(op, "02", 2) == 0) {
1474 stats->G02++;
1475 result = DRILL_G_CWMOVE;
1476 } else if (strncmp(op, "03", 2) == 0) {
1477 stats->G03++;
1478 result = DRILL_G_CCWMOVE;
1479 } else if (strncmp(op, "05", 2) == 0) {
1480 stats->G05++;
1481 result = DRILL_G_DRILL;
1482 } else if (strncmp(op, "90", 2) == 0) {
1483 stats->G90++;
1484 result = DRILL_G_ABSOLUTE;
1485 } else if (strncmp(op, "91", 2) == 0) {
1486 stats->G91++;
1487 result = DRILL_G_INCREMENTAL;
1488 } else if (strncmp(op, "93", 2) == 0) {
1489 stats->G93++;
1490 result = DRILL_G_ZEROSET;
1491 } else {
1492 stats->G_unknown++;
1493 result = DRILL_G_UNKNOWN;
1496 dprintf("<---- ...leaving drill_parse_G_code.\n");
1497 return result;
1499 } /* drill_parse_G_code */
1502 /* -------------------------------------------------------------- */
1503 /* Parse on drill file coordinate.
1504 Returns nothing, but modifies state */
1505 static void
1506 drill_parse_coordinate(gerb_file_t *fd, char firstchar,
1507 gerbv_image_t *image, drill_state_t *state)
1510 int read;
1512 if(state->coordinate_mode == DRILL_MODE_ABSOLUTE) {
1513 if(firstchar == 'X') {
1514 state->curr_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1515 if((read = (char)gerb_fgetc(fd)) == 'Y') {
1516 state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1518 } else {
1519 state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1521 } else if(state->coordinate_mode == DRILL_MODE_INCREMENTAL) {
1522 if(firstchar == 'X') {
1523 state->curr_x += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1524 if((read = (char)gerb_fgetc(fd)) == 'Y') {
1525 state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1527 } else {
1528 state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1532 } /* drill_parse_coordinate */
1535 /* Allocates and returns a new drill_state structure
1536 Returns state pointer on success, NULL on ERROR */
1537 static drill_state_t *
1538 new_state(drill_state_t *state)
1540 state = (drill_state_t *)g_malloc(sizeof(drill_state_t));
1541 if (state != NULL) {
1542 /* Init structure */
1543 memset((void *)state, 0, sizeof(drill_state_t));
1544 state->curr_section = DRILL_NONE;
1545 state->coordinate_mode = DRILL_MODE_ABSOLUTE;
1546 state->origin_x = 0.0;
1547 state->origin_y = 0.0;
1548 state->unit = GERBV_UNIT_UNSPECIFIED;
1549 state->backup_number_format = FMT_000_000; /* only used for METRIC */
1550 state->header_number_format = state->number_format = FMT_00_0000; /* i. e. INCH */
1551 state->autod = 1;
1552 state->decimals = 4;
1555 return state;
1556 } /* new_state */
1559 /* -------------------------------------------------------------- */
1560 /* Reads one double from fd and returns it.
1561 If a decimal point is found, fmt is not used. */
1562 static double
1563 read_double(gerb_file_t *fd, enum number_fmt_t fmt, gerbv_omit_zeros_t omit_zeros, int decimals)
1565 int read;
1566 char temp[DRILL_READ_DOUBLE_SIZE];
1567 int i = 0, ndigits = 0;
1568 double result;
1569 gboolean decimal_point = FALSE;
1570 gboolean sign_prepend = FALSE;
1572 dprintf("%s(%p, %d, %d, %d)\n", __FUNCTION__, fd, fmt, omit_zeros, decimals);
1574 memset(temp, 0, sizeof(temp));
1576 read = gerb_fgetc(fd);
1577 while(read != EOF && i < (DRILL_READ_DOUBLE_SIZE -1) &&
1578 (isdigit(read) || read == '.' || read == ',' || read == '+' || read == '-')) {
1579 if(read == ',' || read == '.') decimal_point = TRUE;
1582 * FIXME -- if we are going to do this, don't we need a
1583 * locale-independent strtod()? I think pcb has one.
1585 if(read == ',')
1586 read = '.'; /* adjust for strtod() */
1588 if(isdigit(read)) ndigits++;
1590 if(read == '-' || read == '+')
1591 sign_prepend = TRUE;
1593 temp[i++] = (char)read;
1594 read = gerb_fgetc(fd);
1596 temp[i] = 0;
1598 gerb_ungetc(fd);
1599 if (decimal_point) {
1600 result = strtod(temp, NULL);
1601 } else {
1602 int wantdigits;
1603 double scale;
1604 char tmp2[DRILL_READ_DOUBLE_SIZE];
1606 memset(tmp2, 0, sizeof(tmp2));
1608 dprintf("%s(): omit_zeros = %d, fmt = %d\n", __FUNCTION__, omit_zeros, fmt);
1609 /* Nothing to take care for when leading zeros are
1610 omitted. */
1611 if (omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
1612 switch (fmt) {
1613 case FMT_00_0000:
1614 wantdigits = 2;
1615 break;
1617 case FMT_000_000:
1618 wantdigits = 3;
1619 break;
1621 case FMT_0000_00:
1622 wantdigits = 4;
1623 break;
1625 case FMT_000_00:
1626 wantdigits = 3;
1627 break;
1629 case FMT_USER:
1630 wantdigits = decimals;
1631 break;
1633 default:
1634 /* cannot happen, just plugs a compiler warning */
1635 fprintf(stderr, "%s(): omit_zeros == GERBV_OMIT_ZEROS_TRAILING but fmt = %d.\n"
1636 "This should never have happened\n", __FUNCTION__, fmt);
1637 return 0;
1640 /* need to add an extra char for '+' or '-' */
1641 if (sign_prepend)
1642 wantdigits++;
1646 * we need at least wantdigits + one for the decimal place
1647 * + one for the terminating null character
1649 if (wantdigits > sizeof(tmp2) - 2) {
1650 fprintf(stderr, "%s(): wantdigits = %d which exceeds the maximum allowed size\n",
1651 __FUNCTION__, wantdigits);
1652 return 0;
1656 * After we have read the correct number of digits
1657 * preceeding the decimal point, insert a decimal point
1658 * and append the rest of the digits.
1660 dprintf("%s(): wantdigits = %d, strlen(\"%s\") = %ld\n",
1661 __FUNCTION__, wantdigits, temp, (long) strlen(temp));
1662 for (i = 0 ; i < wantdigits ; i++) {
1663 tmp2[i] = temp[i];
1665 tmp2[wantdigits] = '.';
1666 for (i = wantdigits ; i <= strlen(temp) ; i++) {
1667 tmp2[i+1] = temp[i];
1669 dprintf("%s(): After dealing with trailing zero supression, convert \"%s\"\n", __FUNCTION__, tmp2);
1670 scale = 1.0;
1672 for (i = 0 ; i <= strlen(tmp2) && i < sizeof (temp) ; i++) {
1673 temp[i] = tmp2[i];
1676 } else {
1679 * figure out the scale factor when we are not suppressing
1680 * trailing zeros.
1682 switch (fmt) {
1683 case FMT_00_0000:
1684 scale = 1E-4;
1685 break;
1687 case FMT_000_000:
1688 scale = 1E-3;
1689 break;
1691 case FMT_000_00:
1692 case FMT_0000_00:
1693 scale = 1E-2;
1694 break;
1696 case FMT_USER:
1697 scale = pow (10.0, -1.0*decimals);
1698 break;
1700 default:
1701 /* cannot happen, just plugs a compiler warning */
1702 fprintf (stderr, "%s(): Unhandled fmt ` %d\n", __FUNCTION__, fmt);
1703 exit (1);
1707 result = strtod(temp, NULL) * scale;
1710 return result;
1711 } /* read_double */
1714 /* -------------------------------------------------------------- */
1715 /* Eats all characters up to and including
1716 the first one of CR or LF */
1717 static void
1718 eat_line(gerb_file_t *fd)
1720 int read = gerb_fgetc(fd);
1722 while(read != 10 && read != 13) {
1723 if (read == EOF) return;
1724 read = gerb_fgetc(fd);
1726 } /* eat_line */
1728 /* -------------------------------------------------------------- */
1729 static char *
1730 get_line(gerb_file_t *fd)
1732 int read = gerb_fgetc(fd);
1733 gchar *retstring;
1734 gchar *tmps=g_strdup("");
1736 while(read != 10 && read != 13) {
1737 if (read == EOF)
1738 return tmps;
1739 retstring = g_strdup_printf("%s%c", tmps, read);
1741 /* since g_strdup_printf allocates memory, we need to free it */
1742 if (tmps) {
1743 g_free (tmps);
1744 tmps = NULL;
1746 tmps = retstring;;
1747 read = gerb_fgetc(fd);
1749 return tmps;
1750 } /* get_line */