1 /*
2  * gEDA - GNU Electronic Design Automation
3  * drill.c
4  * Copyright (C) 2000-2006 Andreas Andersson
5  *
6  * $Id$
7  *
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.
12  *
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.
17  *
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
21  */
22 
23 /** \file drill.c
24     \brief Excellon drill parsing functions
25     \ingroup libgerbv
26 */
27 
28 /*
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
33  */
34 
35 #include "gerbv.h"
36 
37 #include <stdlib.h>
38 
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #endif
42 
43 #include <math.h>  /* pow() */
44 #include <ctype.h>
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 
53 #include "attribute.h"
54 #include "common.h"
55 #include "drill.h"
56 #include "drill_stats.h"
57 
58 /* DEBUG printing.  #define DEBUG 1 in config.h to use this fcn. */
59 #define dprintf if(DEBUG) printf
60 
61 #define MAXL 200
62 #define DRILL_READ_DOUBLE_SIZE 32
63 
64 typedef enum {
65     DRILL_NONE, DRILL_HEADER, DRILL_DATA
66 } drill_file_section_t;
67 
68 typedef enum {
69     DRILL_MODE_ABSOLUTE, DRILL_MODE_INCREMENTAL
70 } drill_coordinate_mode_t;
71 
72 typedef enum {
73     FMT_00_0000	/* INCH */,
74     FMT_000_000	/* METRIC 6-digit, 1 um */,
75     FMT_000_00	/* METRIC 5-digit, 10 um */,
76     FMT_0000_00	/* METRIC 6-digit, 10 um */,
77     FMT_USER	/* User defined format */
78 } number_fmt_t;
79 
80 typedef struct drill_state {
81     double curr_x;
82     double curr_y;
83     int current_tool;
84     drill_file_section_t curr_section;
85     drill_coordinate_mode_t coordinate_mode;
86     double origin_x;
87     double origin_y;
88     gerbv_unit_t unit;
89     /* number_format is used throughout the file itself.
90 
91        header_number_format is used to parse the tool definition C
92        codes within the header.  It is fixed to FMT_00_0000 for INCH
93        measures, and FMT_000_000 (1 um resolution) for metric
94        measures. */
95     number_fmt_t number_format, header_number_format;
96     /* Used as a backup when temporarily switching to INCH. */
97     number_fmt_t backup_number_format;
98 
99     /* 0 means we don't try to autodetect any of the other values */
100     int autod;
101 
102     /* in FMT_USER this specifies the number of digits before the
103      * decimal point when doing trailing zero suppression.  Otherwise
104      * it is the number of digits *after* the decimal
105      * place in the file
106      */
107     int decimals;
108 
109 } drill_state_t;
110 
111 /* Local function prototypes */
112 static drill_g_code_t drill_parse_G_code(gerb_file_t *fd,
113 				gerbv_image_t *image, ssize_t file_line);
114 static drill_m_code_t drill_parse_M_code(gerb_file_t *fd, drill_state_t *state,
115 				gerbv_image_t *image, ssize_t file_line);
116 static int drill_parse_T_code(gerb_file_t *fd, drill_state_t *state,
117 				gerbv_image_t *image, ssize_t file_line);
118 static int drill_parse_header_is_metric(gerb_file_t *fd, drill_state_t *state,
119 				gerbv_image_t *image, ssize_t file_line);
120 static int drill_parse_header_is_inch(gerb_file_t *fd, drill_state_t *state,
121 				gerbv_image_t *image, ssize_t file_line);
122 static int drill_parse_header_is_ici(gerb_file_t *fd, drill_state_t *state,
123 				gerbv_image_t *image, ssize_t file_line);
124 static void drill_parse_coordinate(gerb_file_t *fd, char firstchar,
125 				gerbv_image_t *image, drill_state_t *state,
126 				ssize_t file_line);
127 static drill_state_t *new_state(drill_state_t *state);
128 static double read_double(gerb_file_t *fd, number_fmt_t fmt,
129 				gerbv_omit_zeros_t omit_zeros, int decimals);
130 static void eat_line(gerb_file_t *fd);
131 static char *get_line(gerb_file_t *fd);
132 static int file_check_str(gerb_file_t *fd, const char *str);
133 
134 /* -------------------------------------------------------------- */
135 /* This is the list of specific attributes a drill file may have from
136  * the point of view of parsing it.
137  */
138 
139 enum {
140     SUP_NONE = 0,
141     SUP_LEAD,
142     SUP_TRAIL
143 };
144 
145 static const char *suppression_list[] = {
146     N_("None"),
147     N_("Leading"),
148     N_("Trailing"),
149     0
150 };
151 
152 static const char *units_list[] = {
153     N_("inch"),
154     N_("mm"),
155     0
156 };
157 
158 enum {
159     HA_auto = 0,
160     HA_suppression,
161     HA_xy_units,
162     HA_digits,
163 #if 0
164     HA_tool_units,
165 #endif
166 };
167 
168 static gerbv_HID_Attribute drill_attribute_list[] = {
169     /* This should be first */
170   {N_("autodetect"), N_("Try to autodetect the file format"),
171    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
172 
173   {N_("zero_suppression"), N_("Zero suppression"),
174    HID_Enum, 0, 0, {0, 0, 0}, suppression_list, 0},
175 
176   {N_("units"), N_("Length units"),
177    HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
178 
179   {N_("digits"), N_("Number of digits.  For trailing zero suppression,"
180    " this is the number of digits before the decimal point.  "
181    "Otherwise this is the number of digits after the decimal point."),
182    HID_Integer, 0, 20, {5, 0, 0}, 0, 0},
183 
184 #if 0
185   {"tool_units", "Tool size units",
186    HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
187 #endif
188 };
189 
190 void
drill_attribute_merge(gerbv_HID_Attribute * dest,int ndest,gerbv_HID_Attribute * src,int nsrc)191 drill_attribute_merge (gerbv_HID_Attribute *dest, int ndest, gerbv_HID_Attribute *src, int nsrc)
192 {
193     int i, j;
194 
195     /* Here is a brain dead merge algorithm which should make anyone cringe.
196      * Still, it is simple and we won't merge many attributes and not
197      * many times either.
198      */
199 
200     for (i = 0 ; i < nsrc ; i++) {
201 	/* see if our destination wants this attribute */
202 	j = 0;
203 	while (j < ndest && strcmp (src[i].name, dest[j].name) != 0)
204 	    j++;
205 
206 	/* if we wanted it and it is the same type, copy it over */
207 	if (j < ndest && src[i].type == dest[j].type) {
208 	    dest[j].default_val = src[i].default_val;
209 	} else {
210 	    GERB_MESSAGE("Ignoring \"%s\" attribute for drill file", src[i].name);
211 	}
212     }
213 }
214 
215 static void
drill_update_image_info_min_max_from_bbox(gerbv_image_info_t * info,const gerbv_render_size_t * bbox)216 drill_update_image_info_min_max_from_bbox(gerbv_image_info_t *info,
217 		const gerbv_render_size_t *bbox)
218 {
219     info->min_x = MIN(info->min_x, bbox->left);
220     info->min_y = MIN(info->min_y, bbox->bottom);
221     info->max_x = MAX(info->max_x, bbox->right);
222     info->max_y = MAX(info->max_y, bbox->top);
223 }
224 
225 /*
226  * Adds the actual drill hole to the drawing
227  */
228 static gerbv_net_t *
drill_add_drill_hole(gerbv_image_t * image,drill_state_t * state,gerbv_drill_stats_t * stats,gerbv_net_t * curr_net)229 drill_add_drill_hole (gerbv_image_t *image, drill_state_t *state,
230 		gerbv_drill_stats_t *stats, gerbv_net_t *curr_net)
231 {
232     gerbv_render_size_t *bbox;
233     double r;
234 
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);
238 
239     curr_net->next = g_new0(gerbv_net_t, 1);
240     if (curr_net->next == NULL)
241 	GERB_FATAL_ERROR("malloc curr_net->next failed in %s()",
242 			__FUNCTION__);
243 
244     curr_net = curr_net->next;
245     curr_net->layer = image->layers;
246     curr_net->state = image->states;
247     curr_net->start_x = state->curr_x;
248     curr_net->start_y = state->curr_y;
249     /* KLUDGE. This function isn't allowed to return anything
250        but inches */
251     if(state->unit == GERBV_UNIT_MM) {
252 	curr_net->start_x /= 25.4;
253 	curr_net->start_y /= 25.4;
254 	/* KLUDGE. All images, regardless of input format,
255 	   are returned in INCH format */
256 	curr_net->state->unit = GERBV_UNIT_INCH;
257     }
258 
259     curr_net->stop_x = curr_net->start_x - state->origin_x;
260     curr_net->stop_y = curr_net->start_y - state->origin_y;
261     curr_net->aperture = state->current_tool;
262     curr_net->aperture_state = GERBV_APERTURE_STATE_FLASH;
263 
264     /* Check if aperture is set. Ignore the below instead of
265        causing SEGV... */
266     if(image->aperture[state->current_tool] == NULL)
267 	return curr_net;
268 
269     bbox = &curr_net->boundingBox;
270     r = image->aperture[state->current_tool]->parameter[0] / 2;
271 
272     /* Set boundingBox */
273     bbox->left   = curr_net->start_x - r;
274     bbox->right  = curr_net->start_x + r;
275     bbox->bottom = curr_net->start_y - r;
276     bbox->top    = curr_net->start_y + r;
277 
278     drill_update_image_info_min_max_from_bbox(image->info, bbox);
279 
280     return curr_net;
281 }
282 
283 /* -------------------------------------------------------------- */
284 gerbv_image_t *
parse_drillfile(gerb_file_t * fd,gerbv_HID_Attribute * attr_list,int n_attr,int reload)285 parse_drillfile(gerb_file_t *fd, gerbv_HID_Attribute *attr_list, int n_attr, int reload)
286 {
287     drill_state_t *state = NULL;
288     gerbv_image_t *image = NULL;
289     gerbv_net_t *curr_net = NULL;
290     gerbv_HID_Attribute *hid_attrs;
291     int read;
292     gerbv_drill_stats_t *stats;
293     gchar *tmps;
294     ssize_t file_line = 1;
295 
296     /*
297      * many locales redefine "." as "," and so on, so sscanf and strtod
298      * has problems when reading files using %f format.
299      * Fixes bug #1963618 reported by Lorenzo Marcantonio.
300      */
301     setlocale(LC_NUMERIC, "C" );
302 
303     /* Create new image for this layer */
304     dprintf("In parse_drillfile, about to create image for this layer\n");
305 
306     image = gerbv_create_image(image, "Excellon Drill File");
307     if (image == NULL)
308 	GERB_FATAL_ERROR("malloc image failed in %s()", __FUNCTION__);
309 
310     if (reload && attr_list != NULL) {
311       /* FIXME there should probably just be a function to copy an
312 	 attribute list including using strdup as needed */
313 
314 	image->info->n_attr = n_attr;
315 	image->info->attr_list = gerbv_attribute_dup(attr_list, n_attr);
316 
317     } else {
318 	/* Copy in the default attribute list for drill files.  We make a
319 	 * copy here because we will allow per-layer editing of the
320 	 * attributes.
321 	 */
322 	image->info->n_attr = sizeof (drill_attribute_list) / sizeof (drill_attribute_list[0]);
323 	image->info->attr_list = gerbv_attribute_dup (drill_attribute_list, image->info->n_attr);
324 
325 	/* now merge any project attributes */
326 	drill_attribute_merge (image->info->attr_list, image->info->n_attr,
327 			 attr_list, n_attr);
328     }
329 
330     curr_net = image->netlist;
331     curr_net->layer = image->layers;
332     curr_net->state = image->states;
333     image->layertype = GERBV_LAYERTYPE_DRILL;
334     stats = gerbv_drill_stats_new();
335     if (stats == NULL)
336 	GERB_FATAL_ERROR("malloc stats failed in %s()", __FUNCTION__);
337     image->drill_stats = stats;
338 
339     /* Create local state variable to track photoplotter state */
340     state = new_state(state);
341     if (state == NULL)
342 	GERB_FATAL_ERROR("malloc state failed in %s()", __FUNCTION__);
343 
344     image->format = g_new0(gerbv_format_t, 1);
345     if (image->format == NULL)
346 	GERB_FATAL_ERROR("malloc format failed in %s()", __FUNCTION__);
347 
348     image->format->omit_zeros = GERBV_OMIT_ZEROS_UNSPECIFIED;
349 
350     hid_attrs = image->info->attr_list;
351 
352     if (!hid_attrs[HA_auto].default_val.int_value) {
353 	state->autod = 0;
354 	state->number_format = FMT_USER;
355 	state->decimals = hid_attrs[HA_digits].default_val.int_value;
356 
357 	if (GERBV_UNIT_MM == hid_attrs[HA_xy_units].default_val.int_value)
358 	    state->unit = GERBV_UNIT_MM;
359 
360 	switch (hid_attrs[HA_suppression].default_val.int_value) {
361 	case SUP_LEAD:
362 	    image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
363 	    break;
364 
365 	case SUP_TRAIL:
366 	    image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
367 	    break;
368 
369 	default:
370 	    image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
371 	    break;
372 	}
373     }
374 
375     dprintf("%s():  Starting parsing of drill file \"%s\"\n",
376 		    __FUNCTION__, fd->filename);
377 
378     while ((read = gerb_fgetc(fd)) != EOF) {
379 
380 	switch ((char) read) {
381 
382 	case ';' :
383 	    /* Comment found. Eat rest of line */
384 	    tmps = get_line(fd);
385 	    gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
386 		    _("Comment \"%s\" at line %ld in file \"%s\""),
387 		    tmps, file_line, fd->filename);
388 	    dprintf("    Comment with ';' \"%s\" at line %ld\n",
389 		    tmps, file_line);
390 	    g_free(tmps);
391 	    break;
392 
393 	case 'D' :
394 	    gerb_ungetc (fd);
395 	    tmps = get_line (fd);
396 	    if (strcmp (tmps, "DETECT,ON") == 0 ||
397 		strcmp (tmps, "DETECT,OFF") == 0) {
398 		gchar *tmps2;
399 		gchar *tmps3;
400 		if (strcmp (tmps, "DETECT,ON") == 0)
401 		    tmps3 = "ON";
402 		else
403 		    tmps3 = "OFF";
404 
405 		/* broken tool detect on/off.  Silently ignored. */
406 		if (stats->detect) {
407 		    tmps2 = g_strdup_printf ("%s\n%s", stats->detect, tmps3);
408 		    g_free (stats->detect);
409 		} else {
410 		    tmps2 = g_strdup_printf ("%s", tmps3);
411 		}
412 		stats->detect = tmps2;
413 	    } else {
414 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
415 			_("Unrecognised string \"%s\" in header "
416 			    "at line %ld in file \"%s\""),
417 			tmps, file_line, fd->filename);
418 	    }
419 	    g_free (tmps);
420 	    break;
421 
422 	case 'F' :
423 	    gerb_ungetc (fd);
424 	    tmps = get_line (fd);
425 
426 	    /* Silently ignore FMAT,2.  Not sure what others are allowed */
427 	    if (0 == strcmp (tmps, "FMAT,2")) {
428 		g_free (tmps);
429 		break;
430 	    }
431 
432 	    if (0 == strcmp (tmps, "FMAT,1")) {
433 		gerbv_stats_printf(stats->error_list,
434 			GERBV_MESSAGE_ERROR, -1,
435 			_("File in unsupported format 1 "
436 			    "at line %ld in file \"%s\""),
437 			file_line, fd->filename);
438 
439 		gerbv_destroy_image(image);
440 		g_free (tmps);
441 
442 		return NULL;
443 	    }
444 
445 	    gerbv_stats_printf(stats->error_list,
446 			    GERBV_MESSAGE_ERROR, -1,
447 			    _("Unrecognised string \"%s\" in header "
448 				    "at line %ld in file \"%s\""),
449 			    tmps, file_line, fd->filename);
450 
451 	    g_free (tmps);
452 	    break;
453 
454 	case 'G': {
455 	    /* Most G codes aren't used, for now */
456 	    drill_g_code_t  g_code;
457 
458 	    switch (g_code = drill_parse_G_code(fd, image, file_line)) {
459 
460 	    case DRILL_G_DRILL :
461 		/* Drill mode */
462 		break;
463 
464 	    case DRILL_G_SLOT : {
465 		/* Parse drilled slot end coords */
466 		gerbv_render_size_t *bbox = &curr_net->boundingBox;
467 		double r;
468 
469 		if (EOF == (read = gerb_fgetc(fd))) {
470 		    gerbv_stats_printf(stats->error_list,
471 			    GERBV_MESSAGE_ERROR, -1,
472 			    _("Unexpected EOF found in file \"%s\""),
473 			    fd->filename);
474 		    break;
475 		}
476 
477 		drill_parse_coordinate(fd, read, image, state, file_line);
478 
479 		/* Modify last curr_net as drilled slot */
480 		curr_net->stop_x = state->curr_x;
481 		curr_net->stop_y = state->curr_y;
482 
483 		r = image->aperture[state->current_tool]->parameter[0]/2;
484 
485 		/* Update boundingBox with drilled slot stop_x,y coords */
486 		bbox->left   = MIN(bbox->left,   curr_net->stop_x - r);
487 		bbox->right  = MAX(bbox->right,  curr_net->stop_x + r);
488 		bbox->bottom = MIN(bbox->bottom, curr_net->stop_y - r);
489 		bbox->top    = MAX(bbox->top,    curr_net->stop_y + r);
490 
491 		drill_update_image_info_min_max_from_bbox(image->info, bbox);
492 
493 		if (state->unit == GERBV_UNIT_MM) {
494 		    /* Convert to inches -- internal units */
495 		    curr_net->stop_x /= 25.4;
496 		    curr_net->stop_y /= 25.4;
497 		}
498 		curr_net->aperture_state = GERBV_APERTURE_STATE_ON;
499 
500 		break;
501 	    }
502 
503 	    case DRILL_G_ABSOLUTE :
504 		state->coordinate_mode = DRILL_MODE_ABSOLUTE;
505 		break;
506 
507 	    case DRILL_G_INCREMENTAL :
508 		state->coordinate_mode = DRILL_MODE_INCREMENTAL;
509 		break;
510 
511 	    case DRILL_G_ZEROSET :
512 		if (EOF == (read = gerb_fgetc(fd))) {
513 		    gerbv_stats_printf(stats->error_list,
514 			    GERBV_MESSAGE_ERROR, -1,
515 			    _("Unexpected EOF found in file \"%s\""),
516 			    fd->filename);
517 		    break;
518 		}
519 
520 		drill_parse_coordinate(fd, (char)read, image,
521 			state, file_line);
522 		state->origin_x = state->curr_x;
523 		state->origin_y = state->curr_y;
524 		break;
525 
526 	    case DRILL_G_UNKNOWN:
527 		tmps = get_line(fd);
528 		gerbv_stats_printf(stats->error_list,
529 			GERBV_MESSAGE_ERROR, -1,
530 			_("Unrecognized string \"%s\" found "
531 			    "at line %ld in file \"%s\""),
532 			tmps, file_line, fd->filename);
533 		g_free(tmps);
534 		break;
535 
536 	    default:
537 		eat_line(fd);
538 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
539 			_("Unsupported G%02d (%s) code "
540 			    "at line %ld in file \"%s\""),
541 			g_code, drill_g_code_name(g_code),
542 			file_line, fd->filename);
543 		break;
544 	    }
545 
546 	    break;
547 	}
548 
549 	case 'I':
550 	    gerb_ungetc(fd); /* To compare full string in function or
551 				report full string  */
552 	    if (drill_parse_header_is_inch(fd, state, image, file_line))
553 		break;
554 
555 	    if (drill_parse_header_is_ici(fd, state, image, file_line))
556 		break;
557 
558 	    tmps = get_line(fd);
559 	    gerbv_stats_printf(stats->error_list,
560 		    GERBV_MESSAGE_ERROR, -1,
561 		    _("Unrecognized string \"%s\" found "
562 			"at line %ld in file \"%s\""),
563 		    tmps, file_line, fd->filename);
564 	    g_free(tmps);
565 
566 	    break;
567 
568 	case 'M': {
569 	    int m_code;
570 
571 	    switch (m_code = drill_parse_M_code(fd, state, image, file_line)) {
572 	    case DRILL_M_HEADER :
573 		state->curr_section = DRILL_HEADER;
574 		break;
575 	    case DRILL_M_HEADEREND :
576 		state->curr_section = DRILL_DATA;
577 
578 		if (image->format->omit_zeros == GERBV_OMIT_ZEROS_UNSPECIFIED) {
579 		    /* Excellon says they default to specify leading
580 		       zeros, i.e. omit trailing zeros.	 The Excellon
581 		       files floating around that don't specify the
582 		       leading/trailing zeros in the header seem to
583 		       contradict to this though.
584 
585 		       XXX We should probably ask the user. */
586 
587 		    gerbv_stats_printf(stats->error_list,
588 			    GERBV_MESSAGE_ERROR, -1,
589 			    _("End of Excellon header reached "
590 				"but no leading/trailing zero "
591 				"handling specified "
592 				"at line %ld in file \"%s\""),
593 			    file_line, fd->filename);
594 		    gerbv_stats_printf(stats->error_list,
595 			    GERBV_MESSAGE_WARNING, -1,
596 			    _("Assuming leading zeros in file \"%s\""),
597 			    fd->filename);
598 		    image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
599 		}
600 		break;
601 	    case DRILL_M_METRIC :
602 		if (state->unit == GERBV_UNIT_UNSPECIFIED
603 		&&  state->curr_section != DRILL_HEADER) {
604 		    double size;
605 
606 		    gerbv_stats_printf(stats->error_list,
607 			    GERBV_MESSAGE_WARNING, -1,
608 			    _("M71 code found but no METRIC "
609 				"specification in header "
610 				"at line %ld in file \"%s\""),
611 			    file_line, fd->filename);
612 		    gerbv_stats_printf(stats->error_list,
613 			    GERBV_MESSAGE_WARNING, -1,
614 			    _("Assuming all tool sizes are MM in file \"%s\""),
615 			    fd->filename);
616 
617 		    stats = image->drill_stats;
618 		    for (int tool_num = TOOL_MIN; tool_num < TOOL_MAX;
619 			    tool_num++) {
620 			if (image->aperture && image->aperture[tool_num]) {
621 			    /* First update stats.   Do this before changing drill dias.
622 			     * Maybe also put error into stats? */
623 			    size = image->aperture[tool_num]->parameter[0];
624 			    drill_stats_modify_drill_list(stats->drill_list,
625 							  tool_num,
626 							  size,
627 							  "MM");
628 			    /* Now go back and update all tool dias, since
629 			     * tools are displayed in inch units
630 			     */
631 			    image->aperture[tool_num]->parameter[0] /= 25.4;
632 			}
633 		    }
634 		}
635 		if (state->autod) {
636 		    state->number_format = state->backup_number_format;
637 		    state->unit = GERBV_UNIT_MM;
638 		}
639 		break;
640 	    case DRILL_M_IMPERIAL :
641 		if (state->autod) {
642 		    if (state->number_format != FMT_00_0000)
643 			/* save metric format definition for later */
644 			state->backup_number_format = state->number_format;
645 		    state->number_format = FMT_00_0000;
646 		    state->decimals = 4;
647 		    state->unit = GERBV_UNIT_INCH;
648 		}
649 
650 		break;
651 	    case DRILL_M_CANNEDTEXTX :
652 	    case DRILL_M_CANNEDTEXTY :
653 		tmps = get_line(fd);
654 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
655 			_("Canned text \"%s\" "
656 			    "at line %ld in drill file \"%s\""),
657 			tmps, file_line, fd->filename);
658 		g_free(tmps);
659 		break;
660 	    case DRILL_M_MESSAGELONG :
661 	    case DRILL_M_MESSAGE :
662 		tmps = get_line(fd);
663 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
664 			_("Message \"%s\" embedded "
665 			    "at line %ld in drill file \"%s\""),
666 			tmps, file_line, fd->filename);
667 		g_free(tmps);
668 		break;
669 	    case DRILL_M_PATTERNEND :
670 	    case DRILL_M_TOOLTIPCHECK :
671 		break;
672 
673 	    case DRILL_M_END :
674 		/* M00 has optional arguments */
675 		eat_line(fd);
676 
677 	    case DRILL_M_ENDREWIND :
678 		goto drill_parse_end;
679 		break;
680 
681 	    case DRILL_M_UNKNOWN:
682 		gerb_ungetc(fd); /* To compare full string in function or
683 				    report full string  */
684 		if (drill_parse_header_is_metric(fd, state, image, file_line))
685 		    break;
686 
687 		stats->M_unknown++;
688 		tmps = get_line(fd);
689 		gerbv_stats_printf(stats->error_list,
690 			GERBV_MESSAGE_ERROR, -1,
691 			_("Unrecognized string \"%s\" found "
692 			    "at line %ld in file \"%s\""),
693 			tmps, file_line, fd->filename);
694 		g_free(tmps);
695 
696 		break;
697 
698 	    default:
699 		stats->M_unknown++;
700 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
701 			_("Unsupported M%02d (%s) code found "
702 			    "at line %ld in file \"%s\""),
703 			m_code, _(drill_m_code_name(m_code)),
704 			file_line, fd->filename);
705 		break;
706 	    } /* switch(m_code) */
707 
708 	    break;
709 	} /* case 'M' */
710 
711 	case 'R':
712 	    if (state->curr_section == DRILL_HEADER) {
713 		stats->unknown++;
714 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
715 			_("Not allowed 'R' code in the header "
716 			    "at line %ld in file \"%s\""),
717 			file_line, fd->filename);
718 	    } else {
719 	      double start_x, start_y;
720 	      double step_x, step_y;
721 	      int c;
722 	      int rcnt;
723 	      /*
724 	       * This is the "Repeat hole" command.  Format is:
725 	       * R##[X##][Y##]
726 	       * This repeats the previous hole stepping in the X and
727 	       * Y increments give.  Example:
728 	       * R04X0.1 -- repeats the drill hole 4 times, stepping
729 	       * 0.1" in the X direction.  Note that the X and Y step
730 	       * sizes default to zero if not given and that they use
731 	       * the same format and units as the normal X,Y
732 	       * coordinates.
733 	       */
734 	      stats->R++;
735 
736 	      start_x = state->curr_x;
737 	      start_y = state->curr_y;
738 
739 	      /* figure out how many repeats there are */
740 	      c = gerb_fgetc (fd);
741 	      rcnt = 0;
742 	      while ( '0' <= c && c <= '9') {
743 		rcnt = 10*rcnt + (c - '0');
744 		c = gerb_fgetc (fd);
745 	      }
746 	      dprintf ("working on R code (repeat) with a number of reps equal to %d\n", rcnt);
747 
748 	      step_x = 0.0;
749 	      if (c == 'X') {
750 		step_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
751 		c = gerb_fgetc (fd);
752 	      }
753 
754 	      step_y = 0.0;
755 	      if( c == 'Y') {
756 		  step_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
757 	      } else {
758 		gerb_ungetc (fd);
759 	      }
760 
761 	      dprintf ("Getting ready to repeat the drill %d times with delta_x = %g, delta_y = %g\n", rcnt, step_x, step_y);
762 
763 	      /* spit out the drills */
764 	      for (c = 1 ; c <= rcnt ; c++) {
765 		state->curr_x = start_x + c*step_x;
766 		state->curr_y = start_y + c*step_y;
767 		dprintf ("    Repeat #%d — new location is (%g, %g)\n", c, state->curr_x, state->curr_y);
768 		curr_net = drill_add_drill_hole (image, state, stats, curr_net);
769 	      }
770 
771 	    }
772 
773 	case 'S':
774 	    gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
775 		    _("Ignoring setting spindle speed "
776 			"at line %ld in drill file \"%s\""),
777 		    file_line, fd->filename);
778 	    eat_line(fd);
779 	    break;
780 	case 'T':
781 	    drill_parse_T_code(fd, state, image, file_line);
782 	    break;
783 	case 'V' :
784 	    gerb_ungetc (fd);
785 	    tmps = get_line (fd);
786 	    /* Silently ignore VER,1.  Not sure what others are allowed */
787 	    if (0 != strcmp (tmps, "VER,1")) {
788 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
789 			_("Undefined string \"%s\" in header "
790 			    "at line %ld in file \"%s\""),
791 			tmps, file_line, fd->filename);
792 	    }
793 	    g_free (tmps);
794 	    break;
795 
796 	case 'X':
797 	case 'Y':
798 	    /* Hole coordinate found. Do some parsing */
799 	    drill_parse_coordinate(fd, read, image, state, file_line);
800 
801 	    /* add the new drill hole */
802 	    curr_net = drill_add_drill_hole (image, state, stats, curr_net);
803 	    break;
804 
805 	case '%':
806 	    state->curr_section = DRILL_DATA;
807 	    break;
808 
809 	case '\n' :
810 	    file_line++;
811 
812 	    /* Get <CR> char, if any, from <LF><CR> pair */
813 	    read = gerb_fgetc(fd);
814 	    if (read != '\r' && read != EOF)
815 		    gerb_ungetc(fd);
816 	    break;
817 
818 	case '\r' :
819 	    file_line++;
820 
821 	    /* Get <LF> char, if any, from <CR><LF> pair */
822 	    read = gerb_fgetc(fd);
823 	    if (read != '\n' && read != EOF)
824 		    gerb_ungetc(fd);
825 	    break;
826 
827 	case ' ' :	/* White space */
828 	case '\t' :
829 	    break;
830 
831 	default:
832 	    stats->unknown++;
833 
834 	    if (DRILL_HEADER == state->curr_section) {
835 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
836 			_("Undefined code '%s' (0x%x) found in header "
837 			    "at line %ld in file \"%s\""),
838 			gerbv_escape_char(read), read,
839 			file_line, fd->filename);
840 		gerb_ungetc(fd);
841 
842 		/* Unrecognised crap in the header is thrown away */
843 		tmps = get_line(fd);
844 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
845 			_("Unrecognised string \"%s\" in header "
846 			    "at line %ld in file \"%s\""),
847 			tmps, file_line, fd->filename);
848 	  	g_free (tmps);
849 	    } else {
850 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
851 			_("Ignoring undefined character '%s' (0x%x) "
852 			    "found inside data at line %ld in file \"%s\""),
853 			gerbv_escape_char(read), read, file_line, fd->filename);
854 	    }
855 	}
856     }
857 
858     gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
859 	    _("No EOF found in drill file \"%s\""), fd->filename);
860 
861 drill_parse_end:
862     dprintf ("%s():  Populating file attributes\n", __FUNCTION__);
863 
864     hid_attrs = image->info->attr_list;
865 
866     switch (state->unit) {
867     case GERBV_UNIT_MM:
868 	hid_attrs[HA_xy_units].default_val.int_value = GERBV_UNIT_MM;
869 	break;
870 
871     default:
872 	hid_attrs[HA_xy_units].default_val.int_value = GERBV_UNIT_INCH;
873 	break;
874     }
875 
876     switch (state->number_format) {
877     case FMT_000_00:
878     case FMT_0000_00:
879 	hid_attrs[HA_digits].default_val.int_value = 2;
880 	break;
881 
882     case FMT_000_000:
883 	hid_attrs[HA_digits].default_val.int_value = 3;
884 	break;
885 
886     case FMT_00_0000:
887 	hid_attrs[HA_digits].default_val.int_value = 4;
888 	break;
889 
890     case FMT_USER:
891 	dprintf ("%s():  Keeping user specified number of decimal places (%d)\n",
892 		 __FUNCTION__,
893 		 hid_attrs[HA_digits].default_val.int_value);
894 	break;
895 
896     default:
897 	break;
898     }
899 
900     switch (image->format->omit_zeros) {
901     case GERBV_OMIT_ZEROS_LEADING:
902 	hid_attrs[HA_suppression].default_val.int_value = SUP_LEAD;
903 	break;
904 
905     case GERBV_OMIT_ZEROS_TRAILING:
906 	hid_attrs[HA_suppression].default_val.int_value = SUP_TRAIL;
907 	break;
908 
909     default:
910 	hid_attrs[HA_suppression].default_val.int_value = SUP_NONE;
911 	break;
912     }
913 
914     g_free(state);
915 
916     return image;
917 } /* parse_drillfile */
918 
919 
920 /* -------------------------------------------------------------- */
921 /*
922  * Checks for signs that this is a drill file
923  * Returns TRUE if it is, FALSE if not.
924  */
925 gboolean
drill_file_p(gerb_file_t * fd,gboolean * returnFoundBinary)926 drill_file_p(gerb_file_t *fd, gboolean *returnFoundBinary)
927 {
928     char *buf=NULL, *tbuf;
929     int len = 0;
930     char *letter;
931     int ascii;
932     int zero = 48; /* ascii 0 */
933     int nine = 57; /* ascii 9 */
934     int i;
935     gboolean found_binary = FALSE;
936     gboolean found_M48 = FALSE;
937     gboolean found_M30 = FALSE;
938     gboolean found_percent = FALSE;
939     gboolean found_T = FALSE;
940     gboolean found_X = FALSE;
941     gboolean found_Y = FALSE;
942     gboolean end_comments=FALSE;
943 
944     tbuf = g_malloc(MAXL);
945     if (tbuf == NULL)
946 	GERB_FATAL_ERROR(
947 		"malloc buf failed while checking for drill file in %s()",
948 		__FUNCTION__);
949 
950     while (fgets(tbuf, MAXL, fd->fd) != NULL) {
951 	len = strlen(tbuf);
952 	buf = tbuf;
953 	/* check for comments at top of file.  */
954 	if(!end_comments){
955 		if(g_strstr_len(buf, len, ";")!=NULL){/* comments at top of file  */
956 			for (i = 0; i < len-1; ++i) {
957 				if (buf[i] == '\n'
958 				&&  buf[i+1] != ';'
959 				&&  buf[i+1] != '\r'
960 				&&  buf[i+1] != '\n') {
961 					end_comments = TRUE;
962 					/* Set rest of parser to end of
963 					 * comments */
964 					buf = &tbuf[i+1];
965 
966 				}
967 			}
968 			if(!end_comments)
969 				continue;
970 		}
971 		else
972 			end_comments=TRUE;
973 	}
974 
975 	/* First look through the file for indications of its type */
976 	len = strlen(buf);
977 	/* check that file is not binary (non-printing chars) */
978 	for (i = 0; i < len; i++) {
979 	    ascii = (int) buf[i];
980 	    if ((ascii > 128) || (ascii < 0)) {
981 		found_binary = TRUE;
982 	    }
983 	}
984 
985 	/* Check for M48 = start of drill header */
986 	if (g_strstr_len(buf, len, "M48")) {
987 	    found_M48 = TRUE;
988 	}
989 
990 	/* Check for M30 = end of drill program */
991 	if (g_strstr_len(buf, len, "M30")) {
992 	    if (found_percent) {
993 		found_M30 = TRUE; /* Found M30 after % = good */
994 	    }
995 	}
996 
997 	/* Check for % on its own line at end of header */
998 	if ((letter = g_strstr_len(buf, len, "%")) != NULL) {
999 	    if ((letter[1] ==  '\r') || (letter[1] ==  '\n'))
1000 		found_percent = TRUE;
1001 	}
1002 
1003 	/* Check for T<number> */
1004 	if ((letter = g_strstr_len(buf, len, "T")) != NULL) {
1005 	    if (!found_T && (found_X || found_Y)) {
1006 		found_T = FALSE;  /* Found first T after X or Y */
1007 	    } else {
1008 		if (isdigit( (int) letter[1])) { /* verify next char is digit */
1009 		    found_T = TRUE;
1010 		}
1011 	    }
1012 	}
1013 
1014 	/* look for X<number> or Y<number> */
1015 	if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
1016 	    ascii = (int) letter[1]; /* grab char after X */
1017 	    if ((ascii >= zero) && (ascii <= nine)) {
1018 		found_X = TRUE;
1019 	    }
1020 	}
1021 	if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
1022 	    ascii = (int) letter[1]; /* grab char after Y */
1023 	    if ((ascii >= zero) && (ascii <= nine)) {
1024 		found_Y = TRUE;
1025 	    }
1026 	}
1027     } /* while (fgets(buf, MAXL, fd->fd) */
1028 
1029     rewind(fd->fd);
1030     g_free(tbuf);
1031     *returnFoundBinary = found_binary;
1032 
1033     /* Now form logical expression determining if this is a drill file */
1034     if ( ((found_X || found_Y) && found_T) &&
1035 	 (found_M48 || (found_percent && found_M30)) )
1036 	return TRUE;
1037     else if (found_M48 && found_T && found_percent && found_M30)
1038 	/* Pathological case of drill file with valid header
1039 	   and EOF but no drill XY locations. */
1040 	return TRUE;
1041     else
1042 	return FALSE;
1043 } /* drill_file_p */
1044 
1045 
1046 /* -------------------------------------------------------------- */
1047 /* Parse tool definition. This can get a bit tricky since it can
1048    appear in the header and/or data section.
1049    Returns tool number on success, -1 on error */
1050 static int
drill_parse_T_code(gerb_file_t * fd,drill_state_t * state,gerbv_image_t * image,ssize_t file_line)1051 drill_parse_T_code(gerb_file_t *fd, drill_state_t *state,
1052 			gerbv_image_t *image, ssize_t file_line)
1053 {
1054     int tool_num;
1055     gboolean done = FALSE;
1056     int temp;
1057     double size;
1058     gerbv_drill_stats_t *stats = image->drill_stats;
1059     gerbv_aperture_t *apert;
1060     gchar *tmps;
1061     gchar *string;
1062 
1063     dprintf("---> entering %s()...\n", __FUNCTION__);
1064 
1065     /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
1066        broken headers from Orcad, which is crap */
1067     temp = gerb_fgetc(fd);
1068     dprintf("  Found a char '%s' (0x%02x) after the T\n",
1069 	    gerbv_escape_char(temp), temp);
1070 
1071     /* might be a tool tool change stop switch on/off*/
1072     if((temp == 'C') && ((fd->ptr + 2) < fd->datalen)){
1073     	if(gerb_fgetc(fd) == 'S'){
1074     	    if (gerb_fgetc(fd) == 'T' ){
1075     	  	fd->ptr -= 4;
1076     	  	tmps = get_line(fd++);
1077     	  	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1078 			_("Tool change stop switch found \"%s\" "
1079 			    "at line %ld in file \"%s\""),
1080 			tmps, file_line, fd->filename);
1081 	  	g_free (tmps);
1082 
1083 	  	return -1;
1084 	    }
1085 	    gerb_ungetc(fd);
1086 	}
1087 	gerb_ungetc(fd);
1088     }
1089 
1090     if( !(isdigit(temp) != 0 || temp == '+' || temp =='-') ) {
1091 	if(temp != EOF) {
1092 	    gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1093 		   _("OrCAD bug: Junk text found in place of tool definition"));
1094 	    tmps = get_line(fd);
1095 	    gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1096 		    _("Junk text \"%s\" "
1097 			"at line %ld in file \"%s\""),
1098 		    tmps, file_line, fd->filename);
1099 	    g_free (tmps);
1100 	    gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1101 				  _("Ignoring junk text"));
1102 	}
1103 	return -1;
1104     }
1105     gerb_ungetc(fd);
1106 
1107     tool_num = (int) gerb_fgetint(fd, NULL);
1108     dprintf ("  Handling tool T%d at line %ld\n", tool_num, file_line);
1109 
1110     if (tool_num == 0)
1111 	return tool_num; /* T00 is a command to unload the drill */
1112 
1113     if (tool_num < TOOL_MIN || tool_num >= TOOL_MAX) {
1114 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1115 		_("Out of bounds drill number %d "
1116 		    "at line %ld in file \"%s\""),
1117 		tool_num, file_line, fd->filename);
1118     }
1119 
1120     /* Set the current tool to the correct one */
1121     state->current_tool = tool_num;
1122 
1123     /* Check for a size definition */
1124     temp = gerb_fgetc(fd);
1125 
1126     /* This bit of code looks for a tool definition by scanning for strings
1127      * of form TxxC, TxxF, TxxS.  */
1128     while (!done) {
1129 	switch((char)temp) {
1130 	case 'C':
1131 	    size = read_double(fd, state->header_number_format, GERBV_OMIT_ZEROS_TRAILING, state->decimals);
1132 	    dprintf ("  Read a size of %g\n", size);
1133 
1134 	    if (state->unit == GERBV_UNIT_MM) {
1135 		size /= 25.4;
1136 	    } else if(size >= 4.0) {
1137 		/* If the drill size is >= 4 inches, assume that this
1138 		   must be wrong and that the units are mils.
1139 		   The limit being 4 inches is because the smallest drill
1140 		   I've ever seen used is 0,3mm(about 12mil). Half of that
1141 		   seemed a bit too small a margin, so a third it is */
1142 
1143 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1144 			_("Read a drill of diameter %g inches "
1145 			    "at line %ld in file \"%s\""),
1146 			    size, file_line, fd->filename);
1147 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1148 			_("Assuming units are mils"));
1149 		size /= 1000.0;
1150 	    }
1151 
1152 	    if (size <= 0. || size >= 10000.) {
1153 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1154 			_("Unreasonable drill size %g found for drill %d "
1155 			    "at line %ld in file \"%s\""),
1156 			    size, tool_num, file_line, fd->filename);
1157 	    } else {
1158 		apert = image->aperture[tool_num];
1159 		if (apert != NULL) {
1160 		    /* allow a redefine of a tool only if the new definition is exactly the same.
1161 		     * This avoid lots of spurious complaints with the output of some cad
1162 		     * tools while keeping complaints if there is a true problem
1163 		     */
1164 		    if (apert->parameter[0] != size
1165 		    ||  apert->type != GERBV_APTYPE_CIRCLE
1166 		    ||  apert->nuf_parameters != 1
1167 		    ||  apert->unit != GERBV_UNIT_INCH) {
1168 
1169 			gerbv_stats_printf(stats->error_list,
1170 				GERBV_MESSAGE_ERROR, -1,
1171 				_("Found redefinition of drill %d "
1172 				"at line %ld in file \"%s\""),
1173 				tool_num, file_line, fd->filename);
1174 		    }
1175 		} else {
1176 		    apert = image->aperture[tool_num] =
1177 						g_new0(gerbv_aperture_t, 1);
1178 		    if (apert == NULL)
1179 			GERB_FATAL_ERROR("malloc tool failed in %s()",
1180 					__FUNCTION__);
1181 
1182 		    /* There's really no way of knowing what unit the tools
1183 		       are defined in without sneaking a peek in the rest of
1184 		       the file first. That's done in drill_guess_format() */
1185 		    apert->parameter[0] = size;
1186 		    apert->type = GERBV_APTYPE_CIRCLE;
1187 		    apert->nuf_parameters = 1;
1188 		    apert->unit = GERBV_UNIT_INCH;
1189 		}
1190 	    }
1191 
1192 	    /* Add the tool whose definition we just found into the list
1193 	     * of tools for this layer used to generate statistics. */
1194 	    stats = image->drill_stats;
1195 	    string = g_strdup_printf("%s", (state->unit == GERBV_UNIT_MM ? _("mm") : _("inch")));
1196 	    drill_stats_add_to_drill_list(stats->drill_list,
1197 					  tool_num,
1198 					  state->unit == GERBV_UNIT_MM ? size*25.4 : size,
1199 					  string);
1200 	    g_free(string);
1201 	    break;
1202 
1203 	case 'F':
1204 	case 'S' :
1205 	    /* Silently ignored. They're not important. */
1206 	    gerb_fgetint(fd, NULL);
1207 	    break;
1208 
1209 	default:
1210 	    /* Stop when finding anything but what's expected
1211 	       (and put it back) */
1212 	    gerb_ungetc(fd);
1213 	    done = TRUE;
1214 	    break;
1215 	}  /* switch((char)temp) */
1216 
1217 	temp = gerb_fgetc(fd);
1218 	if (EOF == temp) {
1219 	    gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1220 		    _("Unexpected EOF encountered in header of "
1221 			"drill file \"%s\""), fd->filename);
1222 
1223 	/* Restore new line character for processing */
1224 	if ('\n' == temp || '\r' == temp)
1225 	    gerb_ungetc(fd);
1226 	}
1227     }   /* while(!done) */  /* Done looking at tool definitions */
1228 
1229     /* Catch the tools that aren't defined.
1230        This isn't strictly a good thing, but at least something is shown */
1231     if (apert == NULL) {
1232         double dia;
1233 
1234 	apert = image->aperture[tool_num] = g_new0(gerbv_aperture_t, 1);
1235 	if (apert == NULL)
1236 	    GERB_FATAL_ERROR("malloc tool failed in %s()", __FUNCTION__);
1237 
1238         /* See if we have the tool table */
1239         dia = gerbv_get_tool_diameter(tool_num);
1240         if (dia <= 0) {
1241             /*
1242              * There is no tool. So go out and make some.
1243              * This size calculation is, of course, totally bogus.
1244              */
1245             dia = (double)(16 + 8 * tool_num) / 1000;
1246             /*
1247              * Oooh, this is sooo ugly. But some CAD systems seem to always
1248              * use T00 at the end of the file while others that don't have
1249              * tool definitions inside the file never seem to use T00 at all.
1250              */
1251             if (tool_num != 0) {
1252 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1253 			_("Tool %02d used without being defined "
1254 			    "at line %ld in file \"%s\""),
1255 			tool_num, file_line, fd->filename);
1256 		gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1257 			_("Setting a default size of %g\""), dia);
1258             }
1259 	}
1260 
1261 	apert->type = GERBV_APTYPE_CIRCLE;
1262 	apert->nuf_parameters = 1;
1263 	apert->parameter[0] = dia;
1264 
1265 	/* Add the tool whose definition we just found into the list
1266 	 * of tools for this layer used to generate statistics. */
1267 	if (tool_num != 0) {  /* Only add non-zero tool nums.
1268 			       * Zero = unload command. */
1269 	    stats = image->drill_stats;
1270 	    string = g_strdup_printf("%s",
1271 				     (state->unit == GERBV_UNIT_MM ? _("mm") : _("inch")));
1272 	    drill_stats_add_to_drill_list(stats->drill_list,
1273 					  tool_num,
1274 					  state->unit == GERBV_UNIT_MM ? dia*25.4 : dia,
1275 					  string);
1276 	    g_free(string);
1277 	}
1278     } /* if(image->aperture[tool_num] == NULL) */
1279 
1280     dprintf("<----  ...leaving %s()\n", __FUNCTION__);
1281 
1282     return tool_num;
1283 } /* drill_parse_T_code() */
1284 
1285 
1286 /* -------------------------------------------------------------- */
1287 static drill_m_code_t
drill_parse_M_code(gerb_file_t * fd,drill_state_t * state,gerbv_image_t * image,ssize_t file_line)1288 drill_parse_M_code(gerb_file_t *fd, drill_state_t *state,
1289 	gerbv_image_t *image, ssize_t file_line)
1290 {
1291     gerbv_drill_stats_t *stats = image->drill_stats;
1292     drill_m_code_t m_code;
1293     char op[3];
1294 
1295     dprintf("---> entering %s() ...\n", __FUNCTION__);
1296 
1297     op[0] = gerb_fgetc(fd);
1298     op[1] = gerb_fgetc(fd);
1299     op[2] = '\0';
1300 
1301     if (op[0] == EOF
1302     ||  op[1] == EOF) {
1303 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1304 		_("Unexpected EOF found while parsing M-code in file \"%s\""),
1305 		fd->filename);
1306 
1307 	return DRILL_M_UNKNOWN;
1308     }
1309 
1310     dprintf("  Compare M-code \"%s\" at line %ld\n", op, file_line);
1311 
1312     switch (m_code = atoi(op)) {
1313     case 0:
1314 	/* atoi() return 0 in case of error, recheck string */
1315 	if (0 != strncmp(op, "00", 2)) {
1316 	    m_code = DRILL_M_UNKNOWN;
1317 	    gerb_ungetc(fd);
1318 	    gerb_ungetc(fd);
1319 	    break;
1320 	}
1321 	stats->M00++;
1322 	break;
1323     case 1:
1324 	stats->M01++;
1325 	break;
1326     case 18:
1327 	stats->M18++;
1328 	break;
1329     case 25:
1330 	stats->M25++;
1331 	break;
1332     case 30:
1333 	stats->M30++;
1334 	break;
1335     case 45:
1336 	stats->M45++;
1337 	break;
1338     case 47:
1339 	stats->M47++;
1340 	break;
1341     case 48:
1342 	stats->M48++;
1343 	break;
1344     case 71:
1345 	stats->M71++;
1346 	eat_line(fd);
1347 	break;
1348     case 72:
1349 	stats->M72++;
1350 	eat_line(fd);
1351 	break;
1352     case 95:
1353 	stats->M95++;
1354 	break;
1355     case 97:
1356 	stats->M97++;
1357 	break;
1358     case 98:
1359 	stats->M98++;
1360 	break;
1361 
1362     default:
1363     case DRILL_M_UNKNOWN:
1364 	break;
1365     }
1366 
1367 
1368     dprintf("<----  ...leaving %s()\n", __FUNCTION__);
1369 
1370     return m_code;
1371 } /* drill_parse_M_code() */
1372 
1373 /* -------------------------------------------------------------- */
1374 static int
drill_parse_header_is_metric(gerb_file_t * fd,drill_state_t * state,gerbv_image_t * image,ssize_t file_line)1375 drill_parse_header_is_metric(gerb_file_t *fd, drill_state_t *state,
1376 			gerbv_image_t *image, ssize_t file_line)
1377 {
1378     gerbv_drill_stats_t *stats = image->drill_stats;
1379     char c, op[3];
1380 
1381     dprintf("    %s(): entering\n", __FUNCTION__);
1382 
1383     /* METRIC is not an actual M code but a command that is only
1384      * acceptable within the header.
1385      *
1386      * The syntax is
1387      * METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
1388      */
1389 
1390     if (DRILL_HEADER != state->curr_section)
1391 	return 0;
1392 
1393     switch (file_check_str(fd, "METRIC")) {
1394     case -1:
1395 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1396 		_("Unexpected EOF found while parsing \"%s\" string "
1397 		    "in file \"%s\""), "METRIC", fd->filename);
1398 	return 0;
1399 
1400     case 0:
1401 	return 0;
1402     }
1403 
1404 header_again:
1405 
1406     if (',' != gerb_fgetc(fd)) {
1407 	gerb_ungetc(fd);
1408 	eat_line(fd);
1409     } else {
1410 	/* Is it TZ, LZ, or zerofmt? */
1411 	switch (c = gerb_fgetc(fd)) {
1412 	case 'T':
1413 	case 'L':
1414 	    if ('Z' != gerb_fgetc(fd))
1415 		goto header_junk;
1416 
1417 	    if (c == 'L') {
1418 		dprintf ("    %s(): Detected a file that probably has "
1419 			"trailing zero suppression\n", __FUNCTION__);
1420 		if (state->autod)
1421 		    image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1422 	    } else {
1423 		dprintf ("    %s(): Detected a file that probably has "
1424 			"leading zero suppression\n", __FUNCTION__);
1425 		if (state->autod)
1426 		    image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1427 	    }
1428 
1429 	    if (state->autod) {
1430 		/* Default metric number format is 6-digit, 1 um
1431 		 * resolution.  The header number format (for T#C#
1432 		 * definitions) is fixed to that, while the number
1433 		 * format within the file can differ. */
1434 		state->header_number_format =
1435 		    state->number_format = FMT_000_000;
1436 		state->decimals = 3;
1437 	    }
1438 
1439 	    if (',' == gerb_fgetc(fd))
1440 		/* Anticipate number format will follow */
1441 		goto header_again;
1442 
1443 	    gerb_ungetc(fd);
1444 
1445 	    break;
1446 
1447 	case '0':
1448 	    if ('0' != gerb_fgetc(fd)
1449 	    ||  '0' != gerb_fgetc(fd))
1450 		goto header_junk;
1451 
1452 	    /* We just parsed three 0s, the remainder options
1453 	       so far are: .000 | .00 | 0.00 */
1454 	    op[0] = gerb_fgetc(fd);
1455 	    op[1] = gerb_fgetc(fd);
1456 	    op[2] = '\0';
1457 	    if (EOF == op[0]
1458 	    ||  EOF == op[1])
1459 		goto header_junk;
1460 
1461 	    if (0 == strcmp(op, "0.")) {
1462 		/* expecting FMT_0000_00,
1463 		   two trailing 0s must follow */
1464 		if ('0' != gerb_fgetc(fd)
1465 		||  '0' != gerb_fgetc(fd))
1466 		    goto header_junk;
1467 
1468 		eat_line(fd);
1469 
1470 		if (state->autod) {
1471 		    state->number_format = FMT_0000_00;
1472 		    state->decimals = 2;
1473 		}
1474 		break;
1475 	    }
1476 
1477 	    if (0 != strcmp(op, ".0"))
1478 		goto header_junk;
1479 
1480 	    /* Must be either FMT_000_000 or FMT_000_00, depending
1481 	     * on whether one or two 0s are following */
1482 	    if ('0' != gerb_fgetc(fd))
1483 		goto header_junk;
1484 
1485 	    if ('0' == gerb_fgetc(fd)
1486 	    &&  state->autod) {
1487 		state->number_format = FMT_000_000;
1488 		state->decimals = 3;
1489 	    } else {
1490 		gerb_ungetc(fd);
1491 
1492 		if (state->autod) {
1493 		    state->number_format = FMT_000_00;
1494 		    state->decimals = 2;
1495 		}
1496 	    }
1497 
1498 	    eat_line(fd);
1499 	    break;
1500 
1501 	default:
1502 header_junk:
1503 	    gerb_ungetc(fd);
1504 	    eat_line(fd);
1505 
1506 	    gerbv_stats_printf(stats->error_list,
1507 		    GERBV_MESSAGE_WARNING, -1,
1508 		    _("Found junk after METRIC command "
1509 			"at line %ld in file \"%s\""),
1510 		    file_line, fd->filename);
1511 	    break;
1512 	}
1513     }
1514 
1515     state->unit = GERBV_UNIT_MM;
1516 
1517     return 1;
1518 } /* drill_parse_header_is_metric() */
1519 
1520 /* -------------------------------------------------------------- */
1521 static int
drill_parse_header_is_inch(gerb_file_t * fd,drill_state_t * state,gerbv_image_t * image,ssize_t file_line)1522 drill_parse_header_is_inch(gerb_file_t *fd, drill_state_t *state,
1523 			gerbv_image_t *image, ssize_t file_line)
1524 {
1525     gerbv_drill_stats_t *stats = image->drill_stats;
1526     char c;
1527 
1528     dprintf("    %s(): entering\n", __FUNCTION__);
1529 
1530     if (DRILL_HEADER != state->curr_section)
1531 	return 0;
1532 
1533     switch (file_check_str(fd, "INCH")) {
1534     case -1:
1535 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1536 		_("Unexpected EOF found while parsing \"%s\" string "
1537 		    "in file \"%s\""), "INCH", fd->filename);
1538 	return 0;
1539 
1540     case 0:
1541 	return 0;
1542     }
1543 
1544     /* Look for TZ/LZ */
1545     if (',' != gerb_fgetc(fd)) {
1546 	/* Unget the char in case we just advanced past a new line char */
1547 	gerb_ungetc (fd);
1548     } else {
1549 	c = gerb_fgetc(fd);
1550 	if (c != EOF && 'Z' == gerb_fgetc(fd)) {
1551 	    switch (c) {
1552 	    case 'L':
1553 		if (state->autod) {
1554 		    image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1555 		    state->header_number_format =
1556 			state->number_format = FMT_00_0000;
1557 		    state->decimals = 4;
1558 		}
1559 		break;
1560 
1561 	    case 'T':
1562 		if (state->autod) {
1563 		    image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1564 		    state->header_number_format =
1565 			state->number_format = FMT_00_0000;
1566 		    state->decimals = 4;
1567 		}
1568 		break;
1569 
1570 	    default:
1571 		gerbv_stats_printf(stats->error_list,
1572 			GERBV_MESSAGE_WARNING, -1,
1573 			_("Found junk '%s' after "
1574 			    "INCH command "
1575 			    "at line %ld in file \"%s\""),
1576 			gerbv_escape_char(c),
1577 			file_line, fd->filename);
1578 		break;
1579 	    }
1580 	} else {
1581 	    gerbv_stats_printf(stats->error_list,
1582 		    GERBV_MESSAGE_WARNING, -1,
1583 		    _("Found junk '%s' after INCH command "
1584 			"at line %ld in file \"%s\""),
1585 		    gerbv_escape_char(c),
1586 		    file_line, fd->filename);
1587 	}
1588     }
1589 
1590     state->unit = GERBV_UNIT_INCH;
1591 
1592     return 1;
1593 } /* drill_parse_header_is_inch() */
1594 
1595 /* -------------------------------------------------------------- */
1596 /* Check "ICI" incremental input of coordinates */
1597 static int
drill_parse_header_is_ici(gerb_file_t * fd,drill_state_t * state,gerbv_image_t * image,ssize_t file_line)1598 drill_parse_header_is_ici(gerb_file_t *fd, drill_state_t *state,
1599 			gerbv_image_t *image, ssize_t file_line)
1600 {
1601     gerbv_drill_stats_t *stats = image->drill_stats;
1602 
1603     switch (file_check_str(fd, "ICI,ON")) {
1604     case -1:
1605 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1606 		_("Unexpected EOF found while parsing \"%s\" string "
1607 		    "in file \"%s\""), "ICI,ON", fd->filename);
1608 	return 0;
1609 
1610     case 1:
1611 	state->coordinate_mode = DRILL_MODE_INCREMENTAL;
1612 	return 1;
1613     }
1614 
1615     switch (file_check_str(fd, "ICI,OFF")) {
1616     case -1:
1617 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1618 		_("Unexpected EOF found while parsing \"%s\" string "
1619 		    "in file \"%s\""), "ICI,OFF", fd->filename);
1620 	return 0;
1621 
1622     case 1:
1623 	state->coordinate_mode = DRILL_MODE_ABSOLUTE;
1624 	return 1;
1625     }
1626 
1627     return 0;
1628 } /* drill_parse_header_is_ici() */
1629 
1630 /* -------------------------------------------------------------- */
1631 static drill_g_code_t
drill_parse_G_code(gerb_file_t * fd,gerbv_image_t * image,ssize_t file_line)1632 drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image, ssize_t file_line)
1633 {
1634     char op[3];
1635     drill_g_code_t g_code;
1636     gerbv_drill_stats_t *stats = image->drill_stats;
1637 
1638     dprintf("---> entering %s()...\n", __FUNCTION__);
1639 
1640     op[0] = gerb_fgetc(fd);
1641     op[1] = gerb_fgetc(fd);
1642     op[2] = '\0';
1643 
1644     if (op[0] == EOF
1645     ||  op[1] == EOF) {
1646 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1647 		_("Unexpected EOF found while parsing G-code in file \"%s\""),
1648 		fd->filename);
1649 	return DRILL_G_UNKNOWN;
1650     }
1651 
1652     dprintf("  Compare G-code \"%s\" at line %ld\n", op, file_line);
1653 
1654     switch (g_code = atoi(op)) {
1655     case 0:
1656 	/* atoi() return 0 in case of error, recheck string */
1657 	if (0 != strncmp(op, "00", 2)) {
1658 	    g_code = DRILL_G_UNKNOWN;
1659 	    gerb_ungetc(fd);
1660 	    gerb_ungetc(fd);
1661 	    break;
1662 	}
1663 	stats->G00++;
1664 	break;
1665     case 1:
1666 	stats->G01++;
1667 	break;
1668     case 2:
1669 	stats->G02++;
1670 	break;
1671     case 3:
1672 	stats->G03++;
1673 	break;
1674     case 5:
1675 	stats->G05++;
1676 	break;
1677     case 85:
1678 	stats->G85++;
1679 	break;
1680     case 90:
1681 	stats->G90++;
1682 	break;
1683     case 91:
1684 	stats->G91++;
1685 	break;
1686     case 93:
1687 	stats->G93++;
1688 	break;
1689 
1690     case DRILL_G_UNKNOWN:
1691     default:
1692 	stats->G_unknown++;
1693 	break;
1694     }
1695 
1696     dprintf("<----  ...leaving %s()\n", __FUNCTION__);
1697 
1698     return g_code;
1699 } /* drill_parse_G_code() */
1700 
1701 
1702 /* -------------------------------------------------------------- */
1703 /* Parse on drill file coordinate.
1704    Returns nothing, but modifies state */
1705 static void
drill_parse_coordinate(gerb_file_t * fd,char firstchar,gerbv_image_t * image,drill_state_t * state,ssize_t file_line)1706 drill_parse_coordinate(gerb_file_t *fd, char firstchar,
1707 		       gerbv_image_t *image, drill_state_t *state,
1708 		       ssize_t file_line)
1709 {
1710     int read;
1711     gerbv_drill_stats_t *stats = image->drill_stats;
1712 
1713     if(state->coordinate_mode == DRILL_MODE_ABSOLUTE) {
1714 	if (firstchar == 'X') {
1715 	    state->curr_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1716 	    if ((read = (char)gerb_fgetc(fd)) == 'Y') {
1717 		state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1718 	    } else {
1719 		gerb_ungetc(fd);
1720 	    }
1721 	} else if (firstchar == 'Y') {
1722 	    state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1723 	}
1724     } else if(state->coordinate_mode == DRILL_MODE_INCREMENTAL) {
1725 	if (firstchar == 'X') {
1726 	    state->curr_x += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1727 	    if((read = (char)gerb_fgetc(fd)) == 'Y') {
1728 		state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1729 	    } else {
1730 		gerb_ungetc(fd);
1731 	    }
1732 	} else if (firstchar == 'Y') {
1733 	    state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1734 	}
1735     } else {
1736 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1737 		_("Coordinate mode is not absolute and not incremental "
1738 		    "at line %ld in file \"%s\""),
1739 		file_line, fd->filename);
1740     }
1741 
1742 } /* drill_parse_coordinate */
1743 
1744 
1745 /* Allocates and returns a new drill_state structure
1746    Returns state pointer on success, NULL on ERROR */
1747 static drill_state_t *
new_state(drill_state_t * state)1748 new_state(drill_state_t *state)
1749 {
1750     state = g_new0(drill_state_t, 1);
1751     if (state != NULL) {
1752 	/* Init structure */
1753 	state->curr_section = DRILL_NONE;
1754 	state->coordinate_mode = DRILL_MODE_ABSOLUTE;
1755 	state->origin_x = 0.0;
1756 	state->origin_y = 0.0;
1757 	state->unit = GERBV_UNIT_UNSPECIFIED;
1758 	state->backup_number_format = FMT_000_000; /* only used for METRIC */
1759 	state->header_number_format = state->number_format = FMT_00_0000; /* i. e. INCH */
1760 	state->autod = 1;
1761 	state->decimals = 4;
1762     }
1763 
1764     return state;
1765 } /* new_state */
1766 
1767 
1768 /* -------------------------------------------------------------- */
1769 /* Reads one double from fd and returns it.
1770    If a decimal point is found, fmt is not used. */
1771 static double
read_double(gerb_file_t * fd,number_fmt_t fmt,gerbv_omit_zeros_t omit_zeros,int decimals)1772 read_double(gerb_file_t *fd, number_fmt_t fmt, gerbv_omit_zeros_t omit_zeros, int decimals)
1773 {
1774     int read;
1775     char temp[DRILL_READ_DOUBLE_SIZE];
1776     int i = 0, ndigits = 0;
1777     double result;
1778     gboolean decimal_point = FALSE;
1779     gboolean sign_prepend = FALSE;
1780 
1781     memset(temp, 0, sizeof(temp));
1782 
1783     read = gerb_fgetc(fd);
1784     while(read != EOF && i < (DRILL_READ_DOUBLE_SIZE -1) &&
1785 	  (isdigit(read) || read == '.' || read == ',' || read == '+' || read == '-')) {
1786       if(read == ',' || read == '.') decimal_point = TRUE;
1787 
1788       /*
1789        * FIXME -- if we are going to do this, don't we need a
1790        * locale-independent strtod()?  I think pcb has one.
1791        */
1792       if(read == ',')
1793 	    read = '.'; /* adjust for strtod() */
1794 
1795       if(isdigit(read)) ndigits++;
1796 
1797 	if(read == '-' || read == '+')
1798 	    sign_prepend = TRUE;
1799 
1800       temp[i++] = (char)read;
1801       read = gerb_fgetc(fd);
1802     }
1803 
1804     temp[i] = 0;
1805     gerb_ungetc(fd);
1806 
1807     if (decimal_point) {
1808 	result = strtod(temp, NULL);
1809     } else {
1810 	int wantdigits;
1811 	double scale;
1812 	char tmp2[DRILL_READ_DOUBLE_SIZE];
1813 
1814 	memset(tmp2, 0, sizeof(tmp2));
1815 
1816 	/* Nothing to take care for when leading zeros are
1817 	   omitted. */
1818 	if (omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
1819 	    switch (fmt) {
1820 	    case FMT_00_0000:
1821 	      wantdigits = 2;
1822 	      break;
1823 
1824 	    case FMT_000_000:
1825 	      wantdigits = 3;
1826 	      break;
1827 
1828 	    case FMT_0000_00:
1829 		wantdigits = 4;
1830 		break;
1831 
1832 	    case FMT_000_00:
1833 		wantdigits = 3;
1834 		break;
1835 
1836 	    case FMT_USER:
1837 		wantdigits = decimals;
1838 		break;
1839 
1840 	    default:
1841 	      /* cannot happen, just plugs a compiler warning */
1842 	      fprintf(stderr, _("%s():  omit_zeros == GERBV_OMIT_ZEROS_TRAILING but fmt = %d.\n"
1843 		      "This should never have happened\n"), __FUNCTION__, fmt);
1844 	      return 0;
1845 	    }
1846 
1847 	    /* need to add an extra char for '+' or '-' */
1848 	    if (sign_prepend)
1849 	      wantdigits++;
1850 
1851 
1852 	    /*
1853 	     * we need at least wantdigits + one for the decimal place
1854 	     * + one for the terminating null character
1855 	     */
1856 	    if (wantdigits > sizeof(tmp2) - 2) {
1857 	      fprintf(stderr, _("%s():  wantdigits = %d which exceeds the maximum allowed size\n"),
1858 		      __FUNCTION__, wantdigits);
1859 	      return 0;
1860 	    }
1861 
1862 	    /*
1863 	     * After we have read the correct number of digits
1864 	     * preceeding the decimal point, insert a decimal point
1865 	     * and append the rest of the digits.
1866 	     */
1867 	    dprintf("%s():  wantdigits = %d, strlen(\"%s\") = %ld\n",
1868 		    __FUNCTION__, wantdigits, temp, (long) strlen(temp));
1869 	    for (i = 0 ; i < wantdigits && i < strlen(temp) ; i++) {
1870 	      tmp2[i] = temp[i];
1871 	    }
1872 	    for ( ; i < wantdigits ; i++) {
1873 	      tmp2[i] = '0';
1874 	    }
1875 	    tmp2[i++] = '.';
1876 	    for ( ; i <= strlen(temp) ; i++) {
1877 	      tmp2[i] = temp[i-1];
1878 	    }
1879 	    dprintf("%s():  After dealing with trailing zero suppression, convert \"%s\"\n", __FUNCTION__, tmp2);
1880 	    scale = 1.0;
1881 
1882 	    for (i = 0 ; i <= strlen(tmp2) && i < sizeof (temp) ; i++) {
1883 	      temp[i] = tmp2[i];
1884 	    }
1885 
1886 	} else {
1887 
1888 	  /*
1889 	   * figure out the scale factor when we are not suppressing
1890 	   * trailing zeros.
1891 	   */
1892 	  switch (fmt) {
1893 	  case FMT_00_0000:
1894 	    scale = 1E-4;
1895 	    break;
1896 
1897 	  case FMT_000_000:
1898 	    scale = 1E-3;
1899 	    break;
1900 
1901 	  case FMT_000_00:
1902 	  case FMT_0000_00:
1903 	    scale = 1E-2;
1904 	    break;
1905 
1906 	  case FMT_USER:
1907 	    scale = pow (10.0, -1.0*decimals);
1908 	    break;
1909 
1910 	  default:
1911 	    /* cannot happen, just plugs a compiler warning */
1912 	    fprintf (stderr, _("%s(): Unhandled fmt ` %d\n"), __FUNCTION__, fmt);
1913 	    exit (1);
1914 	  }
1915 	}
1916 
1917 	result = strtod(temp, NULL) * scale;
1918     }
1919 
1920     dprintf("    %s()=%f: fmt=%d, omit_zeros=%d, decimals=%d \n",
1921 		    __FUNCTION__, result, fmt, omit_zeros, decimals);
1922 
1923     return result;
1924 } /* read_double */
1925 
1926 /* -------------------------------------------------------------- */
1927 /* Eats all characters up to and including
1928    the first one of CR or LF */
1929 static void
eat_line(gerb_file_t * fd)1930 eat_line(gerb_file_t *fd)
1931 {
1932     int read;
1933 
1934     do {
1935 	read = gerb_fgetc(fd);
1936     } while (read != '\n' && read != '\r' && read != EOF);
1937 
1938     /* Restore new line character for processing */
1939     if (read != EOF)
1940 	gerb_ungetc(fd);
1941 } /* eat_line */
1942 
1943 /* -------------------------------------------------------------- */
1944 static char *
get_line(gerb_file_t * fd)1945 get_line(gerb_file_t *fd)
1946 {
1947 	int read;
1948 	gchar *retstring;
1949 	gchar *tmps=g_strdup("");
1950 
1951 	read = gerb_fgetc(fd);
1952 	while (read != '\n' && read != '\r' && read != EOF) {
1953 		retstring = g_strdup_printf("%s%c", tmps, read);
1954 
1955 		/* since g_strdup_printf allocates memory, we need to free it */
1956 		if (tmps)  {
1957 			g_free (tmps);
1958 			tmps = NULL;
1959 		}
1960 		tmps = retstring;
1961 		read = gerb_fgetc(fd);
1962 	}
1963 
1964 	/* Restore new line character for processing */
1965 	if (read != EOF)
1966 	    gerb_ungetc(fd);
1967 
1968 	return tmps;
1969 } /* get_line */
1970 
1971 /* -------------------------------------------------------------- */
1972 static int
file_check_str(gerb_file_t * fd,const char * str)1973 file_check_str(gerb_file_t *fd, const char *str)
1974 {
1975     char c;
1976 
1977     for (int i = 0; str[i] != '\0'; i++) {
1978 
1979 	c = gerb_fgetc(fd);
1980 
1981 	if (c == EOF)
1982 	    return -1;
1983 
1984 	if (c != str[i]) {
1985 	    do {
1986 		/* Restore checked string */
1987 		gerb_ungetc(fd);
1988 	    } while (i--);
1989 
1990 	    return 0;
1991 	}
1992     }
1993 
1994     return 1;
1995 }
1996 
1997 /* -------------------------------------------------------------- */
1998 /** Return drill G-code name by code number. */
drill_g_code_name(drill_g_code_t g_code)1999 const char *drill_g_code_name(drill_g_code_t g_code)
2000 {
2001     switch (g_code) {
2002     case DRILL_G_ROUT:
2003 	return N_("rout mode");
2004     case DRILL_G_LINEARMOVE:
2005 	return N_("linear mode");
2006     case DRILL_G_CWMOVE:
2007 	return N_("circular CW mode");
2008     case DRILL_G_CCWMOVE:
2009 	return N_("circular CCW mode");
2010     case DRILL_G_VARIABLEDWELL:
2011 	return N_("variable dwell");
2012     case DRILL_G_DRILL:
2013 	return N_("drill mode");
2014     case DRILL_G_OVERRIDETOOLSPEED:
2015 	return N_("override tool feed or speed");
2016     case DRILL_G_ROUTCIRCLE:
2017 	return N_("routed CW circle");
2018     case DRILL_G_ROUTCIRCLECCW:
2019 	return N_("routed CCW circle");
2020     case DRILL_G_VISTOOL:
2021 	return N_("select vision tool");
2022     case DRILL_G_VISSINGLEPOINTOFFSET:
2023 	return N_("single point vision offset");
2024     case DRILL_G_VISMULTIPOINTTRANS:
2025 	return N_("multipoint vision translation");
2026     case DRILL_G_VISCANCEL:
2027 	return N_("cancel vision translation or offset");
2028     case DRILL_G_VISCORRHOLEDRILL:
2029 	return N_("vision corrected single hole drilling");
2030     case DRILL_G_VISAUTOCALIBRATION:
2031 	return N_("vision system autocalibration");
2032     case DRILL_G_CUTTERCOMPOFF:
2033 	return N_("cutter compensation off");
2034     case DRILL_G_CUTTERCOMPLEFT:
2035 	return N_("cutter compensation left");
2036     case DRILL_G_CUTTERCOMPRIGHT:
2037 	return N_("cutter compensation right");
2038     case DRILL_G_VISSINGLEPOINTOFFSETREL:
2039 	return N_("single point vision relative offset");
2040     case DRILL_G_VISMULTIPOINTTRANSREL:
2041 	return N_("multipoint vision relative translation");
2042     case DRILL_G_VISCANCELREL:
2043 	return N_("cancel vision relative translation or offset");
2044     case DRILL_G_VISCORRHOLEDRILLREL:
2045 	return N_("vision corrected single hole relative drilling");
2046     case DRILL_G_PACKDIP2:
2047 	return N_("dual in line package");
2048     case DRILL_G_PACKDIP:
2049 	return N_("dual in line package");
2050     case DRILL_G_PACK8PINL:
2051 	return N_("eight pin L package");
2052     case DRILL_G_CIRLE:
2053 	return N_("canned circle");
2054     case DRILL_G_SLOT:
2055 	return N_("canned slot");
2056     case DRILL_G_ROUTSLOT:
2057 	return N_("routed step slot");
2058     case DRILL_G_ABSOLUTE:
2059 	return N_("absolute input mode");
2060     case DRILL_G_INCREMENTAL:
2061 	return N_("incremental input mode");
2062     case DRILL_G_ZEROSET:
2063 	return N_("zero set");
2064 
2065     case DRILL_G_UNKNOWN:
2066     default:
2067 	return N_("unknown G-code");
2068     }
2069 } /* drill_g_code_name() */
2070 
2071 /* -------------------------------------------------------------- */
2072 /** Return drill M-code name by code number. */
drill_m_code_name(drill_m_code_t m_code)2073 const char *drill_m_code_name(drill_m_code_t m_code)
2074 {
2075     switch (m_code) {
2076     case DRILL_M_END:
2077 	return N_("end of program");
2078     case DRILL_M_PATTERNEND:
2079 	return N_("pattern end");
2080     case DRILL_M_REPEATPATTERNOFFSET:
2081 	return N_("repeat pattern offset");
2082     case DRILL_M_STOPOPTIONAL:
2083 	return N_("stop optional");
2084     case DRILL_M_SANDREND:
2085 	return N_("step and repeat end");
2086     case DRILL_M_STOPINSPECTION:
2087 	return N_("stop for inspection");
2088     case DRILL_M_ZAXISROUTEPOSITIONDEPTHCTRL:
2089 	return N_("Z-axis rout position with depth control");
2090     case DRILL_M_ZAXISROUTEPOSITION:
2091 	return N_("Z-axis rout position");
2092     case DRILL_M_RETRACTCLAMPING:
2093 	return N_("retract with clamping");
2094     case DRILL_M_RETRACTNOCLAMPING:
2095 	return N_("retract without clamping");
2096     case DRILL_M_TOOLTIPCHECK:
2097 	return N_("tool tip check");
2098     case DRILL_M_PATTERN:
2099 	return N_("pattern start");
2100     case DRILL_M_ENDREWIND:
2101 	return N_("end of program with rewind");
2102     case DRILL_M_MESSAGELONG:
2103 	return N_("long operator message");
2104     case DRILL_M_MESSAGE:
2105 	return N_("operator message");
2106     case DRILL_M_HEADER:
2107 	return N_("header start");
2108     case DRILL_M_VISANDRPATTERN:
2109 	return N_("vision step and repeat pattern start");
2110     case DRILL_M_VISANDRPATTERNREWIND:
2111 	return N_("vision step and repeat rewind");
2112     case DRILL_M_VISANDRPATTERNOFFSETCOUNTERCTRL:
2113 	return N_("vision step and repeat offset counter control");
2114     case DRILL_M_REFSCALING:
2115 	return N_("reference scaling on");
2116     case DRILL_M_REFSCALINGEND:
2117 	return N_("reference scaling off");
2118     case DRILL_M_PECKDRILLING:
2119 	return N_("peck drilling on");
2120     case DRILL_M_PECKDRILLINGEND:
2121 	return N_("peck drilling off");
2122     case DRILL_M_SWAPAXIS:
2123 	return N_("swap axes");
2124     case DRILL_M_METRIC:
2125 	return N_("metric measuring mode");
2126     case DRILL_M_IMPERIAL:
2127 	return N_("inch measuring mode");
2128     case DRILL_M_MIRRORX:
2129 	return N_("mirror image X-axis");
2130     case DRILL_M_MIRRORY:
2131 	return N_("mirror image Y-axis");
2132     case DRILL_M_HEADEREND:
2133 	return N_("header end");
2134     case DRILL_M_CANNEDTEXTX:
2135 	return N_("canned text along X-axis");
2136     case DRILL_M_CANNEDTEXTY:
2137 	return N_("canned text along Y-axis");
2138     case DRILL_M_USERDEFPATTERN:
2139 	return N_("user defined stored pattern");
2140 
2141     case DRILL_M_UNKNOWN:
2142     default:
2143 	return N_("unknown M-code");
2144     }
2145 } /* drill_m_code_name() */
2146