recalculate the bounding boxes when drawing primatives via the libgerbv API
[geda-gerbv.git] / src / drill.c
blob13f0ff793b13ff17d0920b8a87b3393b972594c8
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(string);
627 g_free(tmps);
628 break;
629 case DRILL_M_NOT_IMPLEMENTED :
630 case DRILL_M_ENDPATTERN :
631 case DRILL_M_TIPCHECK :
632 break;
633 case DRILL_M_END :
634 /* M00 has optional arguments */
635 eat_line(fd);
636 case DRILL_M_ENDREWIND :
637 goto drill_parse_end;
638 break;
639 case DRILL_M_METRICHEADER :
640 state->unit = GERBV_UNIT_MM;
641 break;
642 default:
643 drill_stats_add_error(stats->error_list,
645 "Undefined M code found.\n",
646 GERBV_MESSAGE_ERROR);
648 break;
650 case 'R':
651 if(state->curr_section == DRILL_HEADER) {
652 drill_stats_add_error(stats->error_list,
654 "R codes are not allowed in the header.\n",
655 GERBV_MESSAGE_ERROR);
656 } else {
657 double start_x, start_y;
658 double step_x, step_y;
659 int c;
660 int rcnt;
662 * This is the "Repeat hole" command. Format is:
663 * R##[X##][Y##]
664 * This repeats the previous hole stepping in the X and
665 * Y increments give. Example:
666 * R04X0.1 -- repeats the drill hole 4 times, stepping
667 * 0.1" in the X direction. Note that the X and Y step
668 * sizes default to zero if not given and that they use
669 * the same format and units as the normal X,Y
670 * coordinates.
672 stats->R++;
674 start_x = state->curr_x;
675 start_y = state->curr_y;
679 /* figure out how many repeats there are */
680 c = gerb_fgetc (fd);
681 rcnt = 0;
682 while ( '0' <= c && c <= '9') {
683 rcnt = 10*rcnt + (c - '0');
684 c = gerb_fgetc (fd);
686 dprintf ("working on R code (repeat) with a number of reps equal to %d\n", rcnt);
688 step_x = 0.0;
689 if (c == 'X') {
690 step_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
691 c = gerb_fgetc (fd);
694 step_y = 0.0;
695 if( c == 'Y') {
696 step_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
697 } else {
698 gerb_ungetc (fd);
701 dprintf ("Getting ready to repeat the drill %d times with delta_x = %g, delta_y = %g\n", rcnt, step_x, step_y);
703 /* spit out the drills */
704 for (c = 1 ; c <= rcnt ; c++) {
705 state->curr_x = start_x + c*step_x;
706 state->curr_y = start_y + c*step_y;
707 dprintf (" Repeat #%d -- new location is (%g, %g)\n", c, state->curr_x, state->curr_y);
708 curr_net = drill_add_drill_hole (image, state, stats, curr_net);
713 case 'S':
714 drill_stats_add_error(stats->error_list,
716 "Drill file sets spindle speed -- ignoring.\n",
717 GERBV_MESSAGE_NOTE);
718 eat_line(fd);
719 break;
720 case 'T':
721 drill_parse_T_code(fd, state, image);
722 break;
723 case 'V' :
724 gerb_ungetc (fd);
725 tmps = get_line (fd);
726 /* Silently ignore VER,1. Not sure what others are allowed */
727 if (strcmp (tmps, "VER,1") != 0) {
728 string = g_strdup_printf("Undefined header line = '%s'\n",tmps);
729 drill_stats_add_error(stats->error_list,
731 g_strdup_printf("Undefined header line = '%s'\n",tmps),
732 GERBV_MESSAGE_NOTE);
733 g_free(string);
735 g_free (tmps);
736 break;
738 case 'X':
739 case 'Y':
740 /* Hole coordinate found. Do some parsing */
741 drill_parse_coordinate(fd, read, image, state);
743 /* add the new drill hole */
744 curr_net = drill_add_drill_hole (image, state, stats, curr_net);
745 break;
747 case '%':
748 state->curr_section = DRILL_DATA;
749 break;
750 case 10 : /* White space */
751 case 13 :
752 case ' ' :
753 case '\t' :
754 break;
755 default:
756 if(state->curr_section == DRILL_HEADER) {
757 /* Unrecognised crap in the header is thrown away */
758 drill_stats_add_error(stats->error_list,
760 "Undefined codes found in header.\n",
761 GERBV_MESSAGE_ERROR);
762 gerb_ungetc(fd);
763 tmps = get_line(fd);
764 string = g_strdup_printf("Undefined header line = '%s'\n",
765 tmps);
766 drill_stats_add_error(stats->error_list,
768 string,
769 GERBV_MESSAGE_NOTE);
770 g_free(string);
771 g_free (tmps);
772 } else {
773 string = g_strdup_printf("Undefined character '%c' [0x%02x] found inside data, ignoring\n",
774 read, read);
775 drill_stats_add_error(stats->error_list,
777 string,
778 GERBV_MESSAGE_ERROR);
779 g_free(string);
783 drill_stats_add_error(stats->error_list,
785 "No EOF found in drill file.\n",
786 GERBV_MESSAGE_ERROR);
788 drill_parse_end:
789 dprintf ("%s(): Populating file attributes\n", __FUNCTION__);
791 switch (state->unit) {
792 case GERBV_UNIT_MM:
793 image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_MM;
794 break;
796 default:
797 image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_INCH;
798 break;
801 switch (state->number_format) {
802 case FMT_000_00:
803 case FMT_0000_00:
804 image->info->attr_list[HA_digits].default_val.int_value = 2;
805 break;
807 case FMT_000_000:
808 image->info->attr_list[HA_digits].default_val.int_value = 3;
809 break;
811 case FMT_00_0000:
812 image->info->attr_list[HA_digits].default_val.int_value = 4;
813 break;
815 case FMT_USER:
816 dprintf ("%s(): Keeping user specified number of decimal places (%d)\n",
817 __FUNCTION__,
818 image->info->attr_list[HA_digits].default_val.int_value);
819 break;
821 default:
822 break;
825 switch (image->format->omit_zeros) {
826 case GERBV_OMIT_ZEROS_LEADING:
827 image->info->attr_list[HA_supression].default_val.int_value = SUP_LEAD;
828 break;
830 case GERBV_OMIT_ZEROS_TRAILING:
831 image->info->attr_list[HA_supression].default_val.int_value = SUP_TRAIL;
832 break;
834 default:
835 image->info->attr_list[HA_supression].default_val.int_value = SUP_NONE;
836 break;
839 g_free(state);
841 return image;
842 } /* parse_drillfile */
845 /* -------------------------------------------------------------- */
847 * Checks for signs that this is a drill file
848 * Returns TRUE if it is, FALSE if not.
850 gboolean
851 drill_file_p(gerb_file_t *fd, gboolean *returnFoundBinary)
853 char *buf;
854 int len = 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;
868 buf = g_malloc(MAXL);
869 if (buf == NULL)
870 GERB_FATAL_ERROR("malloc buf failed while checking for drill file.\n");
872 while (fgets(buf, MAXL, fd->fd) != NULL) {
873 len = strlen(buf);
875 /* First look through the file for indications of its type */
877 /* check that file is not binary (non-printing chars) */
878 for (i = 0; i < len; i++) {
879 ascii = (int) buf[i];
880 if ((ascii > 128) || (ascii < 0)) {
881 found_binary = TRUE;
885 /* Check for M48 = start of drill header */
886 if (g_strstr_len(buf, len, "M48")) {
887 found_M48 = TRUE;
890 /* Check for M30 = end of drill program */
891 if (g_strstr_len(buf, len, "M30")) {
892 if (found_percent) {
893 found_M30 = TRUE; /* Found M30 after % = good */
897 /* Check for % on its own line at end of header */
898 if ((letter = g_strstr_len(buf, len, "%")) != NULL) {
899 if ((letter[1] == '\r') || (letter[1] == '\n'))
900 found_percent = TRUE;
903 /* Check for T<number> */
904 if ((letter = g_strstr_len(buf, len, "T")) != NULL) {
905 if (!found_T && (found_X || found_Y)) {
906 found_T = FALSE; /* Found first T after X or Y */
907 } else {
908 if (isdigit( (int) letter[1])) { /* verify next char is digit */
909 found_T = TRUE;
914 /* look for X<number> or Y<number> */
915 if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
916 ascii = (int) letter[1]; /* grab char after X */
917 if ((ascii >= zero) && (ascii <= nine)) {
918 found_X = TRUE;
921 if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
922 ascii = (int) letter[1]; /* grab char after Y */
923 if ((ascii >= zero) && (ascii <= nine)) {
924 found_Y = TRUE;
927 } /* while (fgets(buf, MAXL, fd->fd) */
929 rewind(fd->fd);
930 free(buf);
931 *returnFoundBinary = found_binary;
933 /* Now form logical expression determining if this is a drill file */
934 if ( ((found_X || found_Y) && found_T) &&
935 (found_M48 || (found_percent && found_M30)) )
936 return TRUE;
937 else if (found_M48 && found_T && found_percent && found_M30)
938 /* Pathological case of drill file with valid header
939 and EOF but no drill XY locations. */
940 return TRUE;
941 else
942 return FALSE;
943 } /* drill_file_p */
946 /* -------------------------------------------------------------- */
947 /* Parse tool definition. This can get a bit tricky since it can
948 appear in the header and/or data section.
949 Returns tool number on success, -1 on error */
950 static int
951 drill_parse_T_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
953 int tool_num;
954 gboolean done = FALSE;
955 int temp;
956 double size;
957 gerbv_drill_stats_t *stats = image->drill_stats;
958 gchar *tmps;
959 gchar *string;
961 /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
962 broken headers from Orcad, which is crap */
963 temp = gerb_fgetc(fd);
964 dprintf("Found a char %d after the T\n", temp);
966 /* might be a tool tool change stop switch on/off*/
967 if((temp == 'C') && ((fd->ptr + 2) < fd->datalen)){
968 if(gerb_fgetc(fd) == 'S'){
969 if (gerb_fgetc(fd) == 'T' ){
970 fd->ptr -= 4;
971 tmps = get_line(fd++);
972 string = g_strdup_printf("Tool change stop switch found: %s\n", tmps);
973 drill_stats_add_error(stats->error_list,
975 string,
976 GERBV_MESSAGE_NOTE);
977 g_free(string);
978 g_free (tmps);
979 return -1;
981 gerb_ungetc(fd);
983 gerb_ungetc(fd);
986 if( !(isdigit(temp) != 0 || temp == '+' || temp =='-') ) {
987 if(temp != EOF) {
988 drill_stats_add_error(stats->error_list,
990 "Orcad bug: Junk text found in place of tool definition.\n",
991 GERBV_MESSAGE_ERROR);
992 tmps = get_line(fd);
993 string = g_strdup_printf("Junk text = %s\n",
994 tmps);
995 drill_stats_add_error(stats->error_list,
997 string,
998 GERBV_MESSAGE_NOTE);
999 g_free(string);
1000 g_free (tmps);
1001 drill_stats_add_error(stats->error_list,
1003 "Ignorning junk text.\n",
1004 GERBV_MESSAGE_WARNING);
1006 return -1;
1008 gerb_ungetc(fd);
1010 tool_num = (int) gerb_fgetint(fd, NULL);
1011 dprintf ("In %s: handling tool_num = %d\n", __FUNCTION__, tool_num);
1013 if (tool_num == 0)
1014 return tool_num; /* T00 is a command to unload the drill */
1016 if ( (tool_num < TOOL_MIN) || (tool_num >= TOOL_MAX) ) {
1017 string = g_strdup_printf("Drill number out of bounds: %d.\n", tool_num);
1018 drill_stats_add_error(stats->error_list,
1020 string,
1021 GERBV_MESSAGE_ERROR);
1022 g_free(string);
1025 /* Set the current tool to the correct one */
1026 state->current_tool = tool_num;
1028 /* Check for a size definition */
1029 temp = gerb_fgetc(fd);
1031 /* This bit of code looks for a tool definition by scanning for strings
1032 * of form TxxC, TxxF, TxxS. */
1033 while(!done) {
1035 switch((char)temp) {
1036 case 'C':
1037 size = read_double(fd, state->header_number_format, GERBV_OMIT_ZEROS_TRAILING, state->decimals);
1038 dprintf ("%s: Read a size of %g %s\n", __FUNCTION__, size,
1039 state->unit == GERBV_UNIT_MM ? "mm" : "inch");
1040 if(state->unit == GERBV_UNIT_MM) {
1041 size /= 25.4;
1042 } else if(size >= 4.0) {
1043 /* If the drill size is >= 4 inches, assume that this
1044 must be wrong and that the units are mils.
1045 The limit being 4 inches is because the smallest drill
1046 I've ever seen used is 0,3mm(about 12mil). Half of that
1047 seemed a bit too small a margin, so a third it is */
1048 string = g_strdup_printf("Read a drill of diameter %g inches.\n", size);
1049 drill_stats_add_error(stats->error_list,
1051 string,
1052 GERBV_MESSAGE_ERROR);
1053 g_free(string);
1054 string = g_strdup_printf("Assuming units are mils.\n");
1055 drill_stats_add_error(stats->error_list,
1057 string,
1058 GERBV_MESSAGE_WARNING);
1059 g_free(string);
1060 size /= 1000.0;
1063 if(size <= 0. || size >= 10000.) {
1064 string = g_strdup_printf("Unreasonable drill size found for drill %d: %g\n", tool_num, size);
1065 drill_stats_add_error(stats->error_list,
1067 string,
1068 GERBV_MESSAGE_ERROR);
1069 g_free(string);
1070 } else {
1071 if(image->aperture[tool_num] != NULL) {
1072 /* allow a redefine of a tool only if the new definition is exactly the same.
1073 * This avoid lots of spurious complaints with the output of some cad
1074 * tools while keeping complaints if there is a true problem
1076 if (image->aperture[tool_num]->parameter[0] != size ||
1077 image->aperture[tool_num]->type != GERBV_APTYPE_CIRCLE ||
1078 image->aperture[tool_num]->nuf_parameters != 1 ||
1079 image->aperture[tool_num]->unit != GERBV_UNIT_INCH) {
1080 string = g_strdup_printf("Found redefinition of drill %d.\n", tool_num);
1081 drill_stats_add_error(stats->error_list,
1083 string,
1084 GERBV_MESSAGE_ERROR);
1085 g_free(string);
1087 } else {
1088 image->aperture[tool_num] =
1089 (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
1090 if (image->aperture[tool_num] == NULL) {
1091 GERB_FATAL_ERROR("malloc tool failed\n");
1093 /* make sure we zero out all aperature parameters */
1094 memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
1095 /* There's really no way of knowing what unit the tools
1096 are defined in without sneaking a peek in the rest of
1097 the file first. That's done in drill_guess_format() */
1098 image->aperture[tool_num]->parameter[0] = size;
1099 image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1100 image->aperture[tool_num]->nuf_parameters = 1;
1101 image->aperture[tool_num]->unit = GERBV_UNIT_INCH;
1105 /* Add the tool whose definition we just found into the list
1106 * of tools for this layer used to generate statistics. */
1107 stats = image->drill_stats;
1108 string = g_strdup_printf("%s", (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
1109 drill_stats_add_to_drill_list(stats->drill_list,
1110 tool_num,
1111 state->unit == GERBV_UNIT_MM ? size*25.4 : size,
1112 string);
1113 g_free(string);
1114 break;
1116 case 'F':
1117 case 'S' :
1118 /* Silently ignored. They're not important. */
1119 gerb_fgetint(fd, NULL);
1120 break;
1122 default:
1123 /* Stop when finding anything but what's expected
1124 (and put it back) */
1125 gerb_ungetc(fd);
1126 done = TRUE;
1127 break;
1128 } /* switch((char)temp) */
1130 if( (temp = gerb_fgetc(fd)) == EOF) {
1131 drill_stats_add_error(stats->error_list,
1133 "Unexpected EOF encountered header of drill file.\n",
1134 GERBV_MESSAGE_ERROR);
1136 } /* while(!done) */ /* Done looking at tool definitions */
1138 /* Catch the tools that aren't defined.
1139 This isn't strictly a good thing, but at least something is shown */
1140 if(image->aperture[tool_num] == NULL) {
1141 double dia;
1143 image->aperture[tool_num] =
1144 (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
1145 if (image->aperture[tool_num] == NULL) {
1146 GERB_FATAL_ERROR("malloc tool failed\n");
1148 /* make sure we zero out all aperature parameters */
1149 memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
1151 /* See if we have the tool table */
1152 dia = gerbv_get_tool_diameter(tool_num);
1153 if (dia <= 0) {
1155 * There is no tool. So go out and make some.
1156 * This size calculation is, of course, totally bogus.
1158 dia = (double)(16 + 8 * tool_num) / 1000;
1160 * Oooh, this is sooo ugly. But some CAD systems seem to always
1161 * use T00 at the end of the file while others that don't have
1162 * tool definitions inside the file never seem to use T00 at all.
1164 if(tool_num != 0) {
1165 string = g_strdup_printf("Tool %02d used without being defined\n", tool_num);
1166 drill_stats_add_error(stats->error_list,
1168 string,
1169 GERBV_MESSAGE_ERROR);
1170 g_free(string);
1171 string = g_strdup_printf("Setting a default size of %g\"\n", dia);
1172 drill_stats_add_error(stats->error_list,
1174 string,
1175 GERBV_MESSAGE_WARNING);
1176 g_free(string);
1180 image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1181 image->aperture[tool_num]->nuf_parameters = 1;
1182 image->aperture[tool_num]->parameter[0] = dia;
1184 /* Add the tool whose definition we just found into the list
1185 * of tools for this layer used to generate statistics. */
1186 if (tool_num != 0) { /* Only add non-zero tool nums.
1187 * Zero = unload command. */
1188 stats = image->drill_stats;
1189 string = g_strdup_printf("%s",
1190 (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
1191 drill_stats_add_to_drill_list(stats->drill_list,
1192 tool_num,
1193 state->unit == GERBV_UNIT_MM ? dia*25.4 : dia,
1194 string);
1195 g_free(string);
1197 } /* if(image->aperture[tool_num] == NULL) */
1199 return tool_num;
1200 } /* drill_parse_T_code */
1203 /* -------------------------------------------------------------- */
1204 static int
1205 drill_parse_M_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
1207 char op[3] = " ";
1208 int read[3];
1209 gerbv_drill_stats_t *stats = image->drill_stats;
1210 int result=0;
1212 dprintf("---> entering drill_parse_M_code ...\n");
1214 read[0] = gerb_fgetc(fd);
1215 read[1] = gerb_fgetc(fd);
1217 if ((read[0] == EOF) || (read[1] == EOF))
1218 drill_stats_add_error(stats->error_list,
1220 "Unexpected EOF found while parsing M code.\n",
1221 GERBV_MESSAGE_ERROR);
1222 op[0] = read[0], op[1] = read[1], op[2] = 0;
1224 if (strncmp(op, "00", 2) == 0) {
1225 stats->M00++;
1226 result = DRILL_M_END;
1227 } else if (strncmp(op, "01", 2) == 0) {
1228 stats->M01++;
1229 result = DRILL_M_ENDPATTERN;
1230 } else if (strncmp(op, "18", 2) == 0) {
1231 stats->M18++;
1232 result = DRILL_M_TIPCHECK;
1233 } else if (strncmp(op, "25", 2) == 0) {
1234 stats->M25++;
1235 result = DRILL_M_BEGINPATTERN;
1236 } else if (strncmp(op, "31", 2) == 0) {
1237 stats->M31++;
1238 result = DRILL_M_BEGINPATTERN;
1239 } else if (strncmp(op, "30", 2) == 0) {
1240 stats->M30++;
1241 result = DRILL_M_ENDREWIND;
1242 } else if (strncmp(op, "45", 2) == 0) {
1243 stats->M45++;
1244 result = DRILL_M_LONGMESSAGE;
1245 } else if (strncmp(op, "47", 2) == 0) {
1246 stats->M47++;
1247 result = DRILL_M_MESSAGE;
1248 } else if (strncmp(op, "48", 2) == 0) {
1249 stats->M48++;
1250 result = DRILL_M_HEADER;
1251 } else if (strncmp(op, "71", 2) == 0) {
1252 eat_line(fd);
1253 stats->M71++;
1254 result = DRILL_M_METRIC;
1255 } else if (strncmp(op, "72", 2) == 0) {
1256 eat_line(fd);
1257 stats->M72++;
1258 result = DRILL_M_IMPERIAL;
1259 } else if (strncmp(op, "95", 2) == 0) {
1260 stats->M95++;
1261 result = DRILL_M_ENDHEADER;
1262 } else if (strncmp(op, "97", 2) == 0) {
1263 stats->M97++;
1264 result = DRILL_M_CANNEDTEXT;
1265 } else if (strncmp(op, "98", 2) == 0) {
1266 stats->M98++;
1267 return DRILL_M_CANNEDTEXT;
1268 } else if (state->curr_section == DRILL_HEADER &&
1269 strncmp(op, "ET", 2) == 0) {
1270 /* METRIC is not an actual M code but a command that is only
1271 acceptable within the header.
1273 The syntax is
1274 METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
1276 if ('R' == gerb_fgetc(fd) &&
1277 'I' == gerb_fgetc(fd) &&
1278 'C' == gerb_fgetc(fd)) {
1279 again:
1280 if (',' == gerb_fgetc(fd)) {
1281 int c;
1283 /* Is it tzlz, or zerofmt? */
1284 switch ((c = gerb_fgetc(fd))) {
1285 case 'T':
1286 case 'L':
1287 if ('Z' != gerb_fgetc(fd))
1288 goto junk;
1289 if (c == 'L')
1291 dprintf ("%s(): Detected a file that probably has trailing zero supression\n", __FUNCTION__);
1292 if (state->autod)
1294 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1297 else
1299 dprintf ("%s(): Detected a file that probably has leading zero supression\n", __FUNCTION__);
1300 if (state->autod)
1302 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1305 if (state->autod)
1307 /* Default metric number format is 6-digit, 1 um
1308 resolution. The header number format (for T#C#
1309 definitions) is fixed to that, while the number
1310 format within the file can differ. */
1311 state->header_number_format =
1312 state->number_format = FMT_000_000;
1313 state->decimals = 3;
1315 c = gerb_fgetc(fd);
1316 gerb_ungetc(fd);
1317 if (c == ',')
1318 /* anticipate number format will follow */
1319 goto again;
1320 break;
1322 case '0':
1323 if ('0' != gerb_fgetc(fd) ||
1324 '0' != gerb_fgetc(fd))
1325 goto junk;
1326 /* We just parsed three 0s, the remainder options
1327 so far are: .000 | .00 | 0.00 */
1328 read[0] = gerb_fgetc(fd);
1329 read[1] = gerb_fgetc(fd);
1330 if (read[0] == EOF || read[1] == EOF)
1331 goto junk;
1332 op[0] = read[0];
1333 op[1] = read[1];
1334 if (strcmp(op, "0.") == 0) {
1335 /* expecting FMT_0000_00,
1336 two trailing 0s must follow */
1337 if ('0' != gerb_fgetc(fd) ||
1338 '0' != gerb_fgetc(fd))
1339 goto junk;
1340 eat_line(fd);
1341 if (state->autod)
1343 state->number_format = FMT_0000_00;
1344 state->decimals = 2;
1346 break;
1348 if (strcmp(op, ".0") != 0)
1349 goto junk;
1350 /* must be either FMT_000_000 or FMT_000_00, depending
1351 on whether one or two 0s are following */
1352 if ('0' != gerb_fgetc(fd))
1353 goto junk;
1354 if ('0' == gerb_fgetc(fd) && state->autod)
1356 state->number_format = FMT_000_000;
1357 state->decimals = 3;
1359 else {
1360 gerb_ungetc(fd);
1361 if (state->autod)
1363 state->number_format = FMT_000_00;
1364 state->decimals = 2;
1367 eat_line(fd);
1368 break;
1370 default:
1371 junk:
1372 drill_stats_add_error(stats->error_list,
1374 "Found junk after METRIC command\n",
1375 GERBV_MESSAGE_WARNING);
1376 gerb_ungetc(fd);
1377 eat_line(fd);
1378 break;
1380 } else {
1381 gerb_ungetc(fd);
1382 eat_line(fd);
1385 return DRILL_M_METRICHEADER;
1387 } else {
1388 stats->M_unknown++;
1389 result = DRILL_M_UNKNOWN;
1392 dprintf("<---- ...leaving drill_parse_M_code.\n");
1393 return result;
1394 } /* drill_parse_M_code */
1397 /* -------------------------------------------------------------- */
1398 static int
1399 drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image)
1401 char op[3] = " ";
1402 int read[3];
1403 gerbv_drill_stats_t *stats = image->drill_stats;
1404 int result;
1406 dprintf("---> entering drill_parse_G_code ...\n");
1408 read[0] = gerb_fgetc(fd);
1409 read[1] = gerb_fgetc(fd);
1411 if ((read[0] == EOF) || (read[1] == EOF)) {
1412 drill_stats_add_error(stats->error_list,
1414 "Unexpected EOF found while parsing G code.\n",
1415 GERBV_MESSAGE_ERROR);
1418 op[0] = read[0], op[1] = read[1], op[2] = 0;
1420 if (strncmp(op, "00", 2) == 0) {
1421 stats->G00++;
1422 result = DRILL_G_ROUT;
1423 } else if (strncmp(op, "01", 2) == 0) {
1424 stats->G01++;
1425 result = DRILL_G_LINEARMOVE;
1426 } else if (strncmp(op, "02", 2) == 0) {
1427 stats->G02++;
1428 result = DRILL_G_CWMOVE;
1429 } else if (strncmp(op, "03", 2) == 0) {
1430 stats->G03++;
1431 result = DRILL_G_CCWMOVE;
1432 } else if (strncmp(op, "05", 2) == 0) {
1433 stats->G05++;
1434 result = DRILL_G_DRILL;
1435 } else if (strncmp(op, "90", 2) == 0) {
1436 stats->G90++;
1437 result = DRILL_G_ABSOLUTE;
1438 } else if (strncmp(op, "91", 2) == 0) {
1439 stats->G91++;
1440 result = DRILL_G_INCREMENTAL;
1441 } else if (strncmp(op, "93", 2) == 0) {
1442 stats->G93++;
1443 result = DRILL_G_ZEROSET;
1444 } else {
1445 stats->G_unknown++;
1446 result = DRILL_G_UNKNOWN;
1449 dprintf("<---- ...leaving drill_parse_G_code.\n");
1450 return result;
1452 } /* drill_parse_G_code */
1455 /* -------------------------------------------------------------- */
1456 /* Parse on drill file coordinate.
1457 Returns nothing, but modifies state */
1458 static void
1459 drill_parse_coordinate(gerb_file_t *fd, char firstchar,
1460 gerbv_image_t *image, drill_state_t *state)
1463 int read;
1465 if(state->coordinate_mode == DRILL_MODE_ABSOLUTE) {
1466 if(firstchar == 'X') {
1467 state->curr_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1468 if((read = (char)gerb_fgetc(fd)) == 'Y') {
1469 state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1471 } else {
1472 state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1474 } else if(state->coordinate_mode == DRILL_MODE_INCREMENTAL) {
1475 if(firstchar == 'X') {
1476 state->curr_x += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1477 if((read = (char)gerb_fgetc(fd)) == 'Y') {
1478 state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1480 } else {
1481 state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1485 } /* drill_parse_coordinate */
1488 /* Allocates and returns a new drill_state structure
1489 Returns state pointer on success, NULL on ERROR */
1490 static drill_state_t *
1491 new_state(drill_state_t *state)
1493 state = (drill_state_t *)g_malloc(sizeof(drill_state_t));
1494 if (state != NULL) {
1495 /* Init structure */
1496 memset((void *)state, 0, sizeof(drill_state_t));
1497 state->curr_section = DRILL_NONE;
1498 state->coordinate_mode = DRILL_MODE_ABSOLUTE;
1499 state->origin_x = 0.0;
1500 state->origin_y = 0.0;
1501 state->unit = GERBV_UNIT_UNSPECIFIED;
1502 state->backup_number_format = FMT_000_000; /* only used for METRIC */
1503 state->header_number_format = state->number_format = FMT_00_0000; /* i. e. INCH */
1504 state->autod = 1;
1505 state->decimals = 4;
1508 return state;
1509 } /* new_state */
1512 /* -------------------------------------------------------------- */
1513 /* Reads one double from fd and returns it.
1514 If a decimal point is found, fmt is not used. */
1515 static double
1516 read_double(gerb_file_t *fd, enum number_fmt_t fmt, gerbv_omit_zeros_t omit_zeros, int decimals)
1518 int read;
1519 char temp[DRILL_READ_DOUBLE_SIZE];
1520 int i = 0, ndigits = 0;
1521 double result;
1522 gboolean decimal_point = FALSE;
1523 gboolean sign_prepend = FALSE;
1525 dprintf("%s(%p, %d, %d, %d)\n", __FUNCTION__, fd, fmt, omit_zeros, decimals);
1527 memset(temp, 0, sizeof(temp));
1529 read = gerb_fgetc(fd);
1530 while(read != EOF && i < (DRILL_READ_DOUBLE_SIZE -1) &&
1531 (isdigit(read) || read == '.' || read == ',' || read == '+' || read == '-')) {
1532 if(read == ',' || read == '.') decimal_point = TRUE;
1535 * FIXME -- if we are going to do this, don't we need a
1536 * locale-independent strtod()? I think pcb has one.
1538 if(read == ',')
1539 read = '.'; /* adjust for strtod() */
1541 if(isdigit(read)) ndigits++;
1543 if(read == '-' || read == '+')
1544 sign_prepend = TRUE;
1546 temp[i++] = (char)read;
1547 read = gerb_fgetc(fd);
1549 temp[i] = 0;
1551 gerb_ungetc(fd);
1552 if (decimal_point) {
1553 result = strtod(temp, NULL);
1554 } else {
1555 int wantdigits;
1556 double scale;
1557 char tmp2[DRILL_READ_DOUBLE_SIZE];
1559 memset(tmp2, 0, sizeof(tmp2));
1561 dprintf("%s(): omit_zeros = %d, fmt = %d\n", __FUNCTION__, omit_zeros, fmt);
1562 /* Nothing to take care for when leading zeros are
1563 omitted. */
1564 if (omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
1565 switch (fmt) {
1566 case FMT_00_0000:
1567 wantdigits = 2;
1568 break;
1570 case FMT_000_000:
1571 wantdigits = 3;
1572 break;
1574 case FMT_0000_00:
1575 wantdigits = 4;
1576 break;
1578 case FMT_000_00:
1579 wantdigits = 3;
1580 break;
1582 case FMT_USER:
1583 wantdigits = decimals;
1584 break;
1586 default:
1587 /* cannot happen, just plugs a compiler warning */
1588 fprintf(stderr, "%s(): omit_zeros == GERBV_OMIT_ZEROS_TRAILING but fmt = %d.\n"
1589 "This should never have happened\n", __FUNCTION__, fmt);
1590 return 0;
1593 /* need to add an extra char for '+' or '-' */
1594 if (sign_prepend)
1595 wantdigits++;
1599 * we need at least wantdigits + one for the decimal place
1600 * + one for the terminating null character
1602 if (wantdigits > sizeof(tmp2) - 2) {
1603 fprintf(stderr, "%s(): wantdigits = %d which exceeds the maximum allowed size\n",
1604 __FUNCTION__, wantdigits);
1605 return 0;
1609 * After we have read the correct number of digits
1610 * preceeding the decimal point, insert a decimal point
1611 * and append the rest of the digits.
1613 dprintf("%s(): wantdigits = %d, strlen(\"%s\") = %ld\n",
1614 __FUNCTION__, wantdigits, temp, (long) strlen(temp));
1615 for (i = 0 ; i < wantdigits ; i++) {
1616 tmp2[i] = temp[i];
1618 tmp2[wantdigits] = '.';
1619 for (i = wantdigits ; i <= strlen(temp) ; i++) {
1620 tmp2[i+1] = temp[i];
1622 dprintf("%s(): After dealing with trailing zero supression, convert \"%s\"\n", __FUNCTION__, tmp2);
1623 scale = 1.0;
1625 for (i = 0 ; i <= strlen(tmp2) && i < sizeof (temp) ; i++) {
1626 temp[i] = tmp2[i];
1629 } else {
1632 * figure out the scale factor when we are not suppressing
1633 * trailing zeros.
1635 switch (fmt) {
1636 case FMT_00_0000:
1637 scale = 1E-4;
1638 break;
1640 case FMT_000_000:
1641 scale = 1E-3;
1642 break;
1644 case FMT_000_00:
1645 case FMT_0000_00:
1646 scale = 1E-2;
1647 break;
1649 case FMT_USER:
1650 scale = pow (10.0, -1.0*decimals);
1651 break;
1653 default:
1654 /* cannot happen, just plugs a compiler warning */
1655 fprintf (stderr, "%s(): Unhandled fmt ` %d\n", __FUNCTION__, fmt);
1656 exit (1);
1660 result = strtod(temp, NULL) * scale;
1663 return result;
1664 } /* read_double */
1667 /* -------------------------------------------------------------- */
1668 /* Eats all characters up to and including
1669 the first one of CR or LF */
1670 static void
1671 eat_line(gerb_file_t *fd)
1673 int read = gerb_fgetc(fd);
1675 while(read != 10 && read != 13) {
1676 if (read == EOF) return;
1677 read = gerb_fgetc(fd);
1679 } /* eat_line */
1682 /* -------------------------------------------------------------- */
1683 static char *
1684 get_line(gerb_file_t *fd)
1686 int read = gerb_fgetc(fd);
1687 gchar *retstring = "";
1688 gchar *tmps = NULL;
1690 while(read != 10 && read != 13) {
1691 if (read == EOF) return retstring;
1692 retstring = g_strdup_printf("%s%c", retstring, read);
1694 /* since g_strdup_printf allocates memory, we need to free it */
1695 if (tmps) {
1696 g_free (tmps);
1697 tmps = NULL;
1699 tmps = retstring;;
1700 read = gerb_fgetc(fd);
1702 return retstring;
1703 } /* get_line */