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