* fix leak and possible segfault in drill parser (patch by Doug Springer)
[geda-gerbv.git] / src / drill.c
blob16b3c1cdd5dbf3ae0ce0e50b0e077f6c9381a3f0
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;
853 int len = 0;
854 char *letter;
855 int ascii;
856 int zero = 48; /* ascii 0 */
857 int nine = 57; /* ascii 9 */
858 int i;
859 gboolean found_binary = FALSE;
860 gboolean found_M48 = FALSE;
861 gboolean found_M30 = FALSE;
862 gboolean found_percent = FALSE;
863 gboolean found_T = FALSE;
864 gboolean found_X = FALSE;
865 gboolean found_Y = FALSE;
866 gboolean end_comments=FALSE;
868 tbuf = g_malloc(MAXL);
869 if (tbuf == NULL)
870 GERB_FATAL_ERROR("malloc buf failed while checking for drill file.\n");
872 while (fgets(tbuf, MAXL, fd->fd) != NULL) {
873 len = strlen(tbuf);
874 buf=tbuf;
875 /* check for comments at top of file. */
876 if(!end_comments){
877 if(g_strstr_len(buf, len, ";")!=NULL){/* comments at top of file */
878 for (i=0;i<len-1;++i){
879 if(buf[i]=='\n' && buf[i+1] != ';' && buf[i+1] != '\r' && buf[i+1] != '\n'){
880 end_comments=TRUE;
881 buf=&tbuf[i+1]; /* set rest of parser to end of comments */
885 if(!end_comments)
886 continue;
888 else
889 end_comments=TRUE;
892 /* First look through the file for indications of its type */
893 len = strlen(buf);
894 /* check that file is not binary (non-printing chars) */
895 for (i = 0; i < len; i++) {
896 ascii = (int) buf[i];
897 if ((ascii > 128) || (ascii < 0)) {
898 found_binary = TRUE;
902 /* Check for M48 = start of drill header */
903 if (g_strstr_len(buf, len, "M48")) {
904 found_M48 = TRUE;
907 /* Check for M30 = end of drill program */
908 if (g_strstr_len(buf, len, "M30")) {
909 if (found_percent) {
910 found_M30 = TRUE; /* Found M30 after % = good */
914 /* Check for % on its own line at end of header */
915 if ((letter = g_strstr_len(buf, len, "%")) != NULL) {
916 if ((letter[1] == '\r') || (letter[1] == '\n'))
917 found_percent = TRUE;
920 /* Check for T<number> */
921 if ((letter = g_strstr_len(buf, len, "T")) != NULL) {
922 if (!found_T && (found_X || found_Y)) {
923 found_T = FALSE; /* Found first T after X or Y */
924 } else {
925 if (isdigit( (int) letter[1])) { /* verify next char is digit */
926 found_T = TRUE;
931 /* look for X<number> or Y<number> */
932 if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
933 ascii = (int) letter[1]; /* grab char after X */
934 if ((ascii >= zero) && (ascii <= nine)) {
935 found_X = TRUE;
938 if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
939 ascii = (int) letter[1]; /* grab char after Y */
940 if ((ascii >= zero) && (ascii <= nine)) {
941 found_Y = TRUE;
944 } /* while (fgets(buf, MAXL, fd->fd) */
946 rewind(fd->fd);
947 free(tbuf);
948 *returnFoundBinary = found_binary;
950 /* Now form logical expression determining if this is a drill file */
951 if ( ((found_X || found_Y) && found_T) &&
952 (found_M48 || (found_percent && found_M30)) )
953 return TRUE;
954 else if (found_M48 && found_T && found_percent && found_M30)
955 /* Pathological case of drill file with valid header
956 and EOF but no drill XY locations. */
957 return TRUE;
958 else
959 return FALSE;
960 } /* drill_file_p */
963 /* -------------------------------------------------------------- */
964 /* Parse tool definition. This can get a bit tricky since it can
965 appear in the header and/or data section.
966 Returns tool number on success, -1 on error */
967 static int
968 drill_parse_T_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
970 int tool_num;
971 gboolean done = FALSE;
972 int temp;
973 double size;
974 gerbv_drill_stats_t *stats = image->drill_stats;
975 gchar *tmps;
976 gchar *string;
978 /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
979 broken headers from Orcad, which is crap */
980 temp = gerb_fgetc(fd);
981 dprintf("Found a char %d after the T\n", temp);
983 /* might be a tool tool change stop switch on/off*/
984 if((temp == 'C') && ((fd->ptr + 2) < fd->datalen)){
985 if(gerb_fgetc(fd) == 'S'){
986 if (gerb_fgetc(fd) == 'T' ){
987 fd->ptr -= 4;
988 tmps = get_line(fd++);
989 string = g_strdup_printf("Tool change stop switch found: %s\n", tmps);
990 drill_stats_add_error(stats->error_list,
992 string,
993 GERBV_MESSAGE_NOTE);
994 g_free(string);
995 g_free (tmps);
996 return -1;
998 gerb_ungetc(fd);
1000 gerb_ungetc(fd);
1003 if( !(isdigit(temp) != 0 || temp == '+' || temp =='-') ) {
1004 if(temp != EOF) {
1005 drill_stats_add_error(stats->error_list,
1007 "Orcad bug: Junk text found in place of tool definition.\n",
1008 GERBV_MESSAGE_ERROR);
1009 tmps = get_line(fd);
1010 string = g_strdup_printf("Junk text = %s\n",
1011 tmps);
1012 drill_stats_add_error(stats->error_list,
1014 string,
1015 GERBV_MESSAGE_NOTE);
1016 g_free(string);
1017 g_free (tmps);
1018 drill_stats_add_error(stats->error_list,
1020 "Ignorning junk text.\n",
1021 GERBV_MESSAGE_WARNING);
1023 return -1;
1025 gerb_ungetc(fd);
1027 tool_num = (int) gerb_fgetint(fd, NULL);
1028 dprintf ("In %s: handling tool_num = %d\n", __FUNCTION__, tool_num);
1030 if (tool_num == 0)
1031 return tool_num; /* T00 is a command to unload the drill */
1033 if ( (tool_num < TOOL_MIN) || (tool_num >= TOOL_MAX) ) {
1034 string = g_strdup_printf("Drill number out of bounds: %d.\n", tool_num);
1035 drill_stats_add_error(stats->error_list,
1037 string,
1038 GERBV_MESSAGE_ERROR);
1039 g_free(string);
1042 /* Set the current tool to the correct one */
1043 state->current_tool = tool_num;
1045 /* Check for a size definition */
1046 temp = gerb_fgetc(fd);
1048 /* This bit of code looks for a tool definition by scanning for strings
1049 * of form TxxC, TxxF, TxxS. */
1050 while(!done) {
1052 switch((char)temp) {
1053 case 'C':
1054 size = read_double(fd, state->header_number_format, GERBV_OMIT_ZEROS_TRAILING, state->decimals);
1055 dprintf ("%s: Read a size of %g %s\n", __FUNCTION__, size,
1056 state->unit == GERBV_UNIT_MM ? "mm" : "inch");
1057 if(state->unit == GERBV_UNIT_MM) {
1058 size /= 25.4;
1059 } else if(size >= 4.0) {
1060 /* If the drill size is >= 4 inches, assume that this
1061 must be wrong and that the units are mils.
1062 The limit being 4 inches is because the smallest drill
1063 I've ever seen used is 0,3mm(about 12mil). Half of that
1064 seemed a bit too small a margin, so a third it is */
1065 string = g_strdup_printf("Read a drill of diameter %g inches.\n", size);
1066 drill_stats_add_error(stats->error_list,
1068 string,
1069 GERBV_MESSAGE_ERROR);
1070 g_free(string);
1071 string = g_strdup_printf("Assuming units are mils.\n");
1072 drill_stats_add_error(stats->error_list,
1074 string,
1075 GERBV_MESSAGE_WARNING);
1076 g_free(string);
1077 size /= 1000.0;
1080 if(size <= 0. || size >= 10000.) {
1081 string = g_strdup_printf("Unreasonable drill size found for drill %d: %g\n", tool_num, size);
1082 drill_stats_add_error(stats->error_list,
1084 string,
1085 GERBV_MESSAGE_ERROR);
1086 g_free(string);
1087 } else {
1088 if(image->aperture[tool_num] != NULL) {
1089 /* allow a redefine of a tool only if the new definition is exactly the same.
1090 * This avoid lots of spurious complaints with the output of some cad
1091 * tools while keeping complaints if there is a true problem
1093 if (image->aperture[tool_num]->parameter[0] != size ||
1094 image->aperture[tool_num]->type != GERBV_APTYPE_CIRCLE ||
1095 image->aperture[tool_num]->nuf_parameters != 1 ||
1096 image->aperture[tool_num]->unit != GERBV_UNIT_INCH) {
1097 string = g_strdup_printf("Found redefinition of drill %d.\n", tool_num);
1098 drill_stats_add_error(stats->error_list,
1100 string,
1101 GERBV_MESSAGE_ERROR);
1102 g_free(string);
1104 } else {
1105 image->aperture[tool_num] =
1106 (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
1107 if (image->aperture[tool_num] == NULL) {
1108 GERB_FATAL_ERROR("malloc tool failed\n");
1110 /* make sure we zero out all aperature parameters */
1111 memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
1112 /* There's really no way of knowing what unit the tools
1113 are defined in without sneaking a peek in the rest of
1114 the file first. That's done in drill_guess_format() */
1115 image->aperture[tool_num]->parameter[0] = size;
1116 image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1117 image->aperture[tool_num]->nuf_parameters = 1;
1118 image->aperture[tool_num]->unit = GERBV_UNIT_INCH;
1122 /* Add the tool whose definition we just found into the list
1123 * of tools for this layer used to generate statistics. */
1124 stats = image->drill_stats;
1125 string = g_strdup_printf("%s", (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
1126 drill_stats_add_to_drill_list(stats->drill_list,
1127 tool_num,
1128 state->unit == GERBV_UNIT_MM ? size*25.4 : size,
1129 string);
1130 g_free(string);
1131 break;
1133 case 'F':
1134 case 'S' :
1135 /* Silently ignored. They're not important. */
1136 gerb_fgetint(fd, NULL);
1137 break;
1139 default:
1140 /* Stop when finding anything but what's expected
1141 (and put it back) */
1142 gerb_ungetc(fd);
1143 done = TRUE;
1144 break;
1145 } /* switch((char)temp) */
1147 if( (temp = gerb_fgetc(fd)) == EOF) {
1148 drill_stats_add_error(stats->error_list,
1150 "Unexpected EOF encountered header of drill file.\n",
1151 GERBV_MESSAGE_ERROR);
1153 } /* while(!done) */ /* Done looking at tool definitions */
1155 /* Catch the tools that aren't defined.
1156 This isn't strictly a good thing, but at least something is shown */
1157 if(image->aperture[tool_num] == NULL) {
1158 double dia;
1160 image->aperture[tool_num] =
1161 (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
1162 if (image->aperture[tool_num] == NULL) {
1163 GERB_FATAL_ERROR("malloc tool failed\n");
1165 /* make sure we zero out all aperature parameters */
1166 memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
1168 /* See if we have the tool table */
1169 dia = gerbv_get_tool_diameter(tool_num);
1170 if (dia <= 0) {
1172 * There is no tool. So go out and make some.
1173 * This size calculation is, of course, totally bogus.
1175 dia = (double)(16 + 8 * tool_num) / 1000;
1177 * Oooh, this is sooo ugly. But some CAD systems seem to always
1178 * use T00 at the end of the file while others that don't have
1179 * tool definitions inside the file never seem to use T00 at all.
1181 if(tool_num != 0) {
1182 string = g_strdup_printf("Tool %02d used without being defined\n", tool_num);
1183 drill_stats_add_error(stats->error_list,
1185 string,
1186 GERBV_MESSAGE_ERROR);
1187 g_free(string);
1188 string = g_strdup_printf("Setting a default size of %g\"\n", dia);
1189 drill_stats_add_error(stats->error_list,
1191 string,
1192 GERBV_MESSAGE_WARNING);
1193 g_free(string);
1197 image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1198 image->aperture[tool_num]->nuf_parameters = 1;
1199 image->aperture[tool_num]->parameter[0] = dia;
1201 /* Add the tool whose definition we just found into the list
1202 * of tools for this layer used to generate statistics. */
1203 if (tool_num != 0) { /* Only add non-zero tool nums.
1204 * Zero = unload command. */
1205 stats = image->drill_stats;
1206 string = g_strdup_printf("%s",
1207 (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
1208 drill_stats_add_to_drill_list(stats->drill_list,
1209 tool_num,
1210 state->unit == GERBV_UNIT_MM ? dia*25.4 : dia,
1211 string);
1212 g_free(string);
1214 } /* if(image->aperture[tool_num] == NULL) */
1216 return tool_num;
1217 } /* drill_parse_T_code */
1220 /* -------------------------------------------------------------- */
1221 static int
1222 drill_parse_M_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
1224 char op[3] = " ";
1225 int read[3];
1226 gerbv_drill_stats_t *stats = image->drill_stats;
1227 int result=0;
1229 dprintf("---> entering drill_parse_M_code ...\n");
1231 read[0] = gerb_fgetc(fd);
1232 read[1] = gerb_fgetc(fd);
1234 if ((read[0] == EOF) || (read[1] == EOF))
1235 drill_stats_add_error(stats->error_list,
1237 "Unexpected EOF found while parsing M code.\n",
1238 GERBV_MESSAGE_ERROR);
1239 op[0] = read[0], op[1] = read[1], op[2] = 0;
1241 if (strncmp(op, "00", 2) == 0) {
1242 stats->M00++;
1243 result = DRILL_M_END;
1244 } else if (strncmp(op, "01", 2) == 0) {
1245 stats->M01++;
1246 result = DRILL_M_ENDPATTERN;
1247 } else if (strncmp(op, "18", 2) == 0) {
1248 stats->M18++;
1249 result = DRILL_M_TIPCHECK;
1250 } else if (strncmp(op, "25", 2) == 0) {
1251 stats->M25++;
1252 result = DRILL_M_BEGINPATTERN;
1253 } else if (strncmp(op, "31", 2) == 0) {
1254 stats->M31++;
1255 result = DRILL_M_BEGINPATTERN;
1256 } else if (strncmp(op, "30", 2) == 0) {
1257 stats->M30++;
1258 result = DRILL_M_ENDREWIND;
1259 } else if (strncmp(op, "45", 2) == 0) {
1260 stats->M45++;
1261 result = DRILL_M_LONGMESSAGE;
1262 } else if (strncmp(op, "47", 2) == 0) {
1263 stats->M47++;
1264 result = DRILL_M_MESSAGE;
1265 } else if (strncmp(op, "48", 2) == 0) {
1266 stats->M48++;
1267 result = DRILL_M_HEADER;
1268 } else if (strncmp(op, "71", 2) == 0) {
1269 eat_line(fd);
1270 stats->M71++;
1271 result = DRILL_M_METRIC;
1272 } else if (strncmp(op, "72", 2) == 0) {
1273 eat_line(fd);
1274 stats->M72++;
1275 result = DRILL_M_IMPERIAL;
1276 } else if (strncmp(op, "95", 2) == 0) {
1277 stats->M95++;
1278 result = DRILL_M_ENDHEADER;
1279 } else if (strncmp(op, "97", 2) == 0) {
1280 stats->M97++;
1281 result = DRILL_M_CANNEDTEXT;
1282 } else if (strncmp(op, "98", 2) == 0) {
1283 stats->M98++;
1284 return DRILL_M_CANNEDTEXT;
1285 } else if (state->curr_section == DRILL_HEADER &&
1286 strncmp(op, "ET", 2) == 0) {
1287 /* METRIC is not an actual M code but a command that is only
1288 acceptable within the header.
1290 The syntax is
1291 METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
1293 if ('R' == gerb_fgetc(fd) &&
1294 'I' == gerb_fgetc(fd) &&
1295 'C' == gerb_fgetc(fd)) {
1296 again:
1297 if (',' == gerb_fgetc(fd)) {
1298 int c;
1300 /* Is it tzlz, or zerofmt? */
1301 switch ((c = gerb_fgetc(fd))) {
1302 case 'T':
1303 case 'L':
1304 if ('Z' != gerb_fgetc(fd))
1305 goto junk;
1306 if (c == 'L')
1308 dprintf ("%s(): Detected a file that probably has trailing zero supression\n", __FUNCTION__);
1309 if (state->autod)
1311 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1314 else
1316 dprintf ("%s(): Detected a file that probably has leading zero supression\n", __FUNCTION__);
1317 if (state->autod)
1319 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1322 if (state->autod)
1324 /* Default metric number format is 6-digit, 1 um
1325 resolution. The header number format (for T#C#
1326 definitions) is fixed to that, while the number
1327 format within the file can differ. */
1328 state->header_number_format =
1329 state->number_format = FMT_000_000;
1330 state->decimals = 3;
1332 c = gerb_fgetc(fd);
1333 gerb_ungetc(fd);
1334 if (c == ',')
1335 /* anticipate number format will follow */
1336 goto again;
1337 break;
1339 case '0':
1340 if ('0' != gerb_fgetc(fd) ||
1341 '0' != gerb_fgetc(fd))
1342 goto junk;
1343 /* We just parsed three 0s, the remainder options
1344 so far are: .000 | .00 | 0.00 */
1345 read[0] = gerb_fgetc(fd);
1346 read[1] = gerb_fgetc(fd);
1347 if (read[0] == EOF || read[1] == EOF)
1348 goto junk;
1349 op[0] = read[0];
1350 op[1] = read[1];
1351 if (strcmp(op, "0.") == 0) {
1352 /* expecting FMT_0000_00,
1353 two trailing 0s must follow */
1354 if ('0' != gerb_fgetc(fd) ||
1355 '0' != gerb_fgetc(fd))
1356 goto junk;
1357 eat_line(fd);
1358 if (state->autod)
1360 state->number_format = FMT_0000_00;
1361 state->decimals = 2;
1363 break;
1365 if (strcmp(op, ".0") != 0)
1366 goto junk;
1367 /* must be either FMT_000_000 or FMT_000_00, depending
1368 on whether one or two 0s are following */
1369 if ('0' != gerb_fgetc(fd))
1370 goto junk;
1371 if ('0' == gerb_fgetc(fd) && state->autod)
1373 state->number_format = FMT_000_000;
1374 state->decimals = 3;
1376 else {
1377 gerb_ungetc(fd);
1378 if (state->autod)
1380 state->number_format = FMT_000_00;
1381 state->decimals = 2;
1384 eat_line(fd);
1385 break;
1387 default:
1388 junk:
1389 drill_stats_add_error(stats->error_list,
1391 "Found junk after METRIC command\n",
1392 GERBV_MESSAGE_WARNING);
1393 gerb_ungetc(fd);
1394 eat_line(fd);
1395 break;
1397 } else {
1398 gerb_ungetc(fd);
1399 eat_line(fd);
1402 return DRILL_M_METRICHEADER;
1404 } else {
1405 stats->M_unknown++;
1406 result = DRILL_M_UNKNOWN;
1409 dprintf("<---- ...leaving drill_parse_M_code.\n");
1410 return result;
1411 } /* drill_parse_M_code */
1414 /* -------------------------------------------------------------- */
1415 static int
1416 drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image)
1418 char op[3] = " ";
1419 int read[3];
1420 gerbv_drill_stats_t *stats = image->drill_stats;
1421 int result;
1423 dprintf("---> entering drill_parse_G_code ...\n");
1425 read[0] = gerb_fgetc(fd);
1426 read[1] = gerb_fgetc(fd);
1428 if ((read[0] == EOF) || (read[1] == EOF)) {
1429 drill_stats_add_error(stats->error_list,
1431 "Unexpected EOF found while parsing G code.\n",
1432 GERBV_MESSAGE_ERROR);
1435 op[0] = read[0], op[1] = read[1], op[2] = 0;
1437 if (strncmp(op, "00", 2) == 0) {
1438 stats->G00++;
1439 result = DRILL_G_ROUT;
1440 } else if (strncmp(op, "01", 2) == 0) {
1441 stats->G01++;
1442 result = DRILL_G_LINEARMOVE;
1443 } else if (strncmp(op, "02", 2) == 0) {
1444 stats->G02++;
1445 result = DRILL_G_CWMOVE;
1446 } else if (strncmp(op, "03", 2) == 0) {
1447 stats->G03++;
1448 result = DRILL_G_CCWMOVE;
1449 } else if (strncmp(op, "05", 2) == 0) {
1450 stats->G05++;
1451 result = DRILL_G_DRILL;
1452 } else if (strncmp(op, "90", 2) == 0) {
1453 stats->G90++;
1454 result = DRILL_G_ABSOLUTE;
1455 } else if (strncmp(op, "91", 2) == 0) {
1456 stats->G91++;
1457 result = DRILL_G_INCREMENTAL;
1458 } else if (strncmp(op, "93", 2) == 0) {
1459 stats->G93++;
1460 result = DRILL_G_ZEROSET;
1461 } else {
1462 stats->G_unknown++;
1463 result = DRILL_G_UNKNOWN;
1466 dprintf("<---- ...leaving drill_parse_G_code.\n");
1467 return result;
1469 } /* drill_parse_G_code */
1472 /* -------------------------------------------------------------- */
1473 /* Parse on drill file coordinate.
1474 Returns nothing, but modifies state */
1475 static void
1476 drill_parse_coordinate(gerb_file_t *fd, char firstchar,
1477 gerbv_image_t *image, drill_state_t *state)
1480 int read;
1482 if(state->coordinate_mode == DRILL_MODE_ABSOLUTE) {
1483 if(firstchar == 'X') {
1484 state->curr_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1485 if((read = (char)gerb_fgetc(fd)) == 'Y') {
1486 state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1488 } else {
1489 state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1491 } else if(state->coordinate_mode == DRILL_MODE_INCREMENTAL) {
1492 if(firstchar == 'X') {
1493 state->curr_x += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1494 if((read = (char)gerb_fgetc(fd)) == 'Y') {
1495 state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1497 } else {
1498 state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1502 } /* drill_parse_coordinate */
1505 /* Allocates and returns a new drill_state structure
1506 Returns state pointer on success, NULL on ERROR */
1507 static drill_state_t *
1508 new_state(drill_state_t *state)
1510 state = (drill_state_t *)g_malloc(sizeof(drill_state_t));
1511 if (state != NULL) {
1512 /* Init structure */
1513 memset((void *)state, 0, sizeof(drill_state_t));
1514 state->curr_section = DRILL_NONE;
1515 state->coordinate_mode = DRILL_MODE_ABSOLUTE;
1516 state->origin_x = 0.0;
1517 state->origin_y = 0.0;
1518 state->unit = GERBV_UNIT_UNSPECIFIED;
1519 state->backup_number_format = FMT_000_000; /* only used for METRIC */
1520 state->header_number_format = state->number_format = FMT_00_0000; /* i. e. INCH */
1521 state->autod = 1;
1522 state->decimals = 4;
1525 return state;
1526 } /* new_state */
1529 /* -------------------------------------------------------------- */
1530 /* Reads one double from fd and returns it.
1531 If a decimal point is found, fmt is not used. */
1532 static double
1533 read_double(gerb_file_t *fd, enum number_fmt_t fmt, gerbv_omit_zeros_t omit_zeros, int decimals)
1535 int read;
1536 char temp[DRILL_READ_DOUBLE_SIZE];
1537 int i = 0, ndigits = 0;
1538 double result;
1539 gboolean decimal_point = FALSE;
1540 gboolean sign_prepend = FALSE;
1542 dprintf("%s(%p, %d, %d, %d)\n", __FUNCTION__, fd, fmt, omit_zeros, decimals);
1544 memset(temp, 0, sizeof(temp));
1546 read = gerb_fgetc(fd);
1547 while(read != EOF && i < (DRILL_READ_DOUBLE_SIZE -1) &&
1548 (isdigit(read) || read == '.' || read == ',' || read == '+' || read == '-')) {
1549 if(read == ',' || read == '.') decimal_point = TRUE;
1552 * FIXME -- if we are going to do this, don't we need a
1553 * locale-independent strtod()? I think pcb has one.
1555 if(read == ',')
1556 read = '.'; /* adjust for strtod() */
1558 if(isdigit(read)) ndigits++;
1560 if(read == '-' || read == '+')
1561 sign_prepend = TRUE;
1563 temp[i++] = (char)read;
1564 read = gerb_fgetc(fd);
1566 temp[i] = 0;
1568 gerb_ungetc(fd);
1569 if (decimal_point) {
1570 result = strtod(temp, NULL);
1571 } else {
1572 int wantdigits;
1573 double scale;
1574 char tmp2[DRILL_READ_DOUBLE_SIZE];
1576 memset(tmp2, 0, sizeof(tmp2));
1578 dprintf("%s(): omit_zeros = %d, fmt = %d\n", __FUNCTION__, omit_zeros, fmt);
1579 /* Nothing to take care for when leading zeros are
1580 omitted. */
1581 if (omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
1582 switch (fmt) {
1583 case FMT_00_0000:
1584 wantdigits = 2;
1585 break;
1587 case FMT_000_000:
1588 wantdigits = 3;
1589 break;
1591 case FMT_0000_00:
1592 wantdigits = 4;
1593 break;
1595 case FMT_000_00:
1596 wantdigits = 3;
1597 break;
1599 case FMT_USER:
1600 wantdigits = decimals;
1601 break;
1603 default:
1604 /* cannot happen, just plugs a compiler warning */
1605 fprintf(stderr, "%s(): omit_zeros == GERBV_OMIT_ZEROS_TRAILING but fmt = %d.\n"
1606 "This should never have happened\n", __FUNCTION__, fmt);
1607 return 0;
1610 /* need to add an extra char for '+' or '-' */
1611 if (sign_prepend)
1612 wantdigits++;
1616 * we need at least wantdigits + one for the decimal place
1617 * + one for the terminating null character
1619 if (wantdigits > sizeof(tmp2) - 2) {
1620 fprintf(stderr, "%s(): wantdigits = %d which exceeds the maximum allowed size\n",
1621 __FUNCTION__, wantdigits);
1622 return 0;
1626 * After we have read the correct number of digits
1627 * preceeding the decimal point, insert a decimal point
1628 * and append the rest of the digits.
1630 dprintf("%s(): wantdigits = %d, strlen(\"%s\") = %ld\n",
1631 __FUNCTION__, wantdigits, temp, (long) strlen(temp));
1632 for (i = 0 ; i < wantdigits ; i++) {
1633 tmp2[i] = temp[i];
1635 tmp2[wantdigits] = '.';
1636 for (i = wantdigits ; i <= strlen(temp) ; i++) {
1637 tmp2[i+1] = temp[i];
1639 dprintf("%s(): After dealing with trailing zero supression, convert \"%s\"\n", __FUNCTION__, tmp2);
1640 scale = 1.0;
1642 for (i = 0 ; i <= strlen(tmp2) && i < sizeof (temp) ; i++) {
1643 temp[i] = tmp2[i];
1646 } else {
1649 * figure out the scale factor when we are not suppressing
1650 * trailing zeros.
1652 switch (fmt) {
1653 case FMT_00_0000:
1654 scale = 1E-4;
1655 break;
1657 case FMT_000_000:
1658 scale = 1E-3;
1659 break;
1661 case FMT_000_00:
1662 case FMT_0000_00:
1663 scale = 1E-2;
1664 break;
1666 case FMT_USER:
1667 scale = pow (10.0, -1.0*decimals);
1668 break;
1670 default:
1671 /* cannot happen, just plugs a compiler warning */
1672 fprintf (stderr, "%s(): Unhandled fmt ` %d\n", __FUNCTION__, fmt);
1673 exit (1);
1677 result = strtod(temp, NULL) * scale;
1680 return result;
1681 } /* read_double */
1684 /* -------------------------------------------------------------- */
1685 /* Eats all characters up to and including
1686 the first one of CR or LF */
1687 static void
1688 eat_line(gerb_file_t *fd)
1690 int read = gerb_fgetc(fd);
1692 while(read != 10 && read != 13) {
1693 if (read == EOF) return;
1694 read = gerb_fgetc(fd);
1696 } /* eat_line */
1698 /* -------------------------------------------------------------- */
1699 static char *
1700 get_line(gerb_file_t *fd)
1702 int read = gerb_fgetc(fd);
1703 gchar *retstring;
1704 gchar *tmps=g_strdup("");
1706 while(read != 10 && read != 13) {
1707 if (read == EOF)
1708 return tmps;
1709 retstring = g_strdup_printf("%s%c", tmps, read);
1711 /* since g_strdup_printf allocates memory, we need to free it */
1712 if (tmps) {
1713 g_free (tmps);
1714 tmps = NULL;
1716 tmps = retstring;;
1717 read = gerb_fgetc(fd);
1719 return tmps;
1720 } /* get_line */