gerbv
drill.c
Go to the documentation of this file.
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 
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(), atan2(), hypot(), cos(), sin(), fabs(), floor() */
44 #include <float.h> /* DBL_EPSILON */
45 #include <ctype.h>
46 
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 
54 #include "attribute.h"
55 #include "common.h"
56 #include "drill.h"
57 #include "drill_stats.h"
58 #include "gerber.h"
59 
60 /* DEBUG printing. #define DEBUG 1 in config.h to use this fcn. */
61 #undef DPRINTF
62 #define DPRINTF(...) do { if (DEBUG) printf(__VA_ARGS__); } while (0)
63 
64 #define MAXL 200
65 #define DRILL_READ_DOUBLE_SIZE 32
66 
67 typedef enum {
68  DRILL_NONE, DRILL_HEADER, DRILL_DATA
69 } drill_file_section_t;
70 
71 typedef enum {
72  DRILL_MODE_ABSOLUTE, DRILL_MODE_INCREMENTAL
73 } drill_coordinate_mode_t;
74 
75 typedef enum {
76  FMT_00_0000 /* INCH */,
77  FMT_000_000 /* METRIC 6-digit, 1 um */,
78  FMT_000_00 /* METRIC 5-digit, 10 um */,
79  FMT_0000_00 /* METRIC 6-digit, 10 um */,
80  FMT_USER /* User defined format */
81 } number_fmt_t;
82 
83 typedef struct drill_pattern_entry {
84  double x;
85  double y;
86  double prev_x; /* start point for route segments */
87  double prev_y;
88  int tool;
89  gboolean is_route; /* FALSE = drill hole, TRUE = route segment */
90 } drill_pattern_entry_t;
91 
92 typedef struct drill_state {
93  double curr_x;
94  double curr_y;
95  int current_tool;
96  drill_file_section_t curr_section;
97  drill_coordinate_mode_t coordinate_mode;
98  double origin_x;
99  double origin_y;
100  gerbv_unit_t unit;
101  /* number_format is used throughout the file itself.
102 
103  header_number_format is used to parse the tool definition C
104  codes within the header. It is fixed to FMT_00_0000 for INCH
105  measures, and FMT_000_000 (1 um resolution) for metric
106  measures. */
107  number_fmt_t number_format, header_number_format;
108  /* Used as a backup when temporarily switching to INCH. */
109  number_fmt_t backup_number_format;
110 
111  /* 0 means we don't try to autodetect any of the other values */
112  int autod;
113 
114  /* in FMT_USER this specifies the number of digits before the
115  * decimal point when doing trailing zero suppression. Otherwise
116  * it is the number of digits *after* the decimal
117  * place in the file
118  */
119  int decimals;
120 
121  /* When FILE_FORMAT=N:M is parsed, stores N (digits before decimal).
122  * Used to correctly set decimals when trailing zero suppression is
123  * later specified via INCH,LZ or METRIC,LZ, because decimals must be
124  * N (not M) in FMT_USER + trailing suppression mode. */
125  int digits_before;
126 
127  /* Routing mode state (G00/G01 + M15/M16/M17) */
128  drill_g_code_t route_mode; /* DRILL_G_DRILL (default), DRILL_G_ROUT, or DRILL_G_LINEARMOVE */
129  gboolean tool_down; /* TRUE after M15, FALSE after M16/M17 */
130 
131  /* Arc center offsets for G02/G03 (I/J values, relative to start) */
132  double delta_cp_x;
133  double delta_cp_y;
134 
135  /* Arc radius for G02/G03 (A value) — alternative to I/J */
136  double arc_radius;
137  gboolean found_arc_radius;
138 
139  /* Pattern recording state (M25/M01/M02) */
140  gboolean in_pattern;
141  GArray *pattern_buffer; /* Array of drill_pattern_entry_t */
142 
143  /* Axis transform state (M70/M80/M90) — toggles */
144  gboolean swap_axis; /* M70 */
145  gboolean mirror_x; /* M80 */
146  gboolean mirror_y; /* M90 */
147 
148 } drill_state_t;
149 
150 /* Local function prototypes */
151 static drill_g_code_t drill_parse_G_code(gerb_file_t *fd,
152  gerbv_image_t *image, unsigned int file_line);
153 static drill_m_code_t drill_parse_M_code(gerb_file_t *fd, drill_state_t *state,
154  gerbv_image_t *image, unsigned int file_line);
155 static int drill_parse_T_code(gerb_file_t *fd, drill_state_t *state,
156  gerbv_image_t *image, unsigned int file_line);
157 static int drill_parse_header_is_metric(gerb_file_t *fd, drill_state_t *state,
158  gerbv_image_t *image, unsigned int file_line);
159 static int drill_parse_header_is_metric_comment(gerb_file_t *fd, drill_state_t *state,
160  gerbv_image_t *image, unsigned int file_line);
161 static int drill_parse_header_is_inch(gerb_file_t *fd, drill_state_t *state,
162  gerbv_image_t *image, unsigned int file_line);
163 static int drill_parse_header_is_ici(gerb_file_t *fd, drill_state_t *state,
164  gerbv_image_t *image, unsigned int file_line);
165 static void drill_parse_coordinate(gerb_file_t *fd, char firstchar,
166  gerbv_image_t *image, drill_state_t *state,
167  unsigned int file_line);
168 static drill_state_t *new_state(drill_state_t *state);
169 static gerbv_net_t *drill_add_route_segment(gerbv_image_t *image,
170  drill_state_t *state,
171  gerbv_drill_stats_t *stats,
172  gerbv_net_t *curr_net,
173  double prev_x, double prev_y);
174 static gerbv_net_t *drill_add_arc_segment(gerbv_image_t *image,
175  drill_state_t *state,
176  gerbv_drill_stats_t *stats,
177  gerbv_net_t *curr_net,
178  double prev_x, double prev_y);
179 static double read_double(gerb_file_t *fd, number_fmt_t fmt,
180  gerbv_omit_zeros_t omit_zeros, int decimals);
181 static void eat_line(gerb_file_t *fd);
182 static void eat_whitespace(gerb_file_t *fd);
183 static char *get_line(gerb_file_t *fd);
184 static int file_check_str(gerb_file_t *fd, const char *str);
185 
186 /* -------------------------------------------------------------- */
187 /* This is the list of specific attributes a drill file may have from
188  * the point of view of parsing it.
189  */
190 
191 enum {
192  SUP_NONE = 0,
193  SUP_LEAD,
194  SUP_TRAIL
195 };
196 
197 static const char *suppression_list[] = {
198  N_("None"),
199  N_("Leading"),
200  N_("Trailing"),
201  0
202 };
203 
204 static const char *units_list[] = {
205  N_("inch"),
206  N_("mm"),
207  0
208 };
209 
210 enum {
211  HA_auto = 0,
212  HA_suppression,
213  HA_xy_units,
214  HA_digits,
215 #if 0
216  HA_tool_units,
217 #endif
218 };
219 
220 static gerbv_HID_Attribute drill_attribute_list[] = {
221  /* This should be first */
222  {N_("autodetect"), N_("Try to autodetect the file format"),
223  HID_Boolean, 0, 0, {1, 0, 0}, 0, 0, 0},
224 
225  {N_("zero_suppression"), N_("Zero suppression"),
226  HID_Enum, 0, 0, {0, 0, 0}, suppression_list, 0, 0},
227 
228  {N_("units"), N_("Length units"),
229  HID_Enum, 0, 0, {0, 0, 0}, units_list, 0, 0},
230 
231  {N_("digits"), N_("Number of digits. For trailing zero suppression,"
232  " this is the number of digits before the decimal point. "
233  "Otherwise this is the number of digits after the decimal point."),
234  HID_Integer, 0, 20, {5, 0, 0}, 0, 0, 0},
235 
236 #if 0
237  {"tool_units", "Tool size units",
238  HID_Enum, 0, 0, {0, 0, 0}, units_list, 0, 0},
239 #endif
240 };
241 
242 void
243 drill_attribute_merge (gerbv_HID_Attribute *dest, int ndest, gerbv_HID_Attribute *src, int nsrc)
244 {
245  int i, j;
246 
247  /* Here is a brain dead merge algorithm which should make anyone cringe.
248  * Still, it is simple and we won't merge many attributes and not
249  * many times either.
250  */
251 
252  for (i = 0 ; i < nsrc ; i++) {
253  /* see if our destination wants this attribute */
254  j = 0;
255  while (j < ndest && strcmp (src[i].name, dest[j].name) != 0)
256  j++;
257 
258  /* if we wanted it and it is the same type, copy it over */
259  if (j < ndest && src[i].type == dest[j].type) {
260  dest[j].default_val = src[i].default_val;
261  } else {
262  GERB_MESSAGE("Ignoring \"%s\" attribute for drill file", src[i].name);
263  }
264  }
265 }
266 
267 static void
268 drill_update_image_info_min_max_from_bbox(gerbv_image_info_t *info,
269  const gerbv_render_size_t *bbox)
270 {
271  info->min_x = MIN(info->min_x, bbox->left);
272  info->min_y = MIN(info->min_y, bbox->bottom);
273  info->max_x = MAX(info->max_x, bbox->right);
274  info->max_y = MAX(info->max_y, bbox->top);
275 }
276 
277 /*
278  * Adds the actual drill hole to the drawing
279  */
280 static gerbv_net_t *
281 drill_add_drill_hole (gerbv_image_t *image, drill_state_t *state,
282  gerbv_drill_stats_t *stats, gerbv_net_t *curr_net)
283 {
284  gerbv_render_size_t *bbox;
285  double r;
286 
287  /* Add one to drill stats for the current tool */
288  drill_stats_increment_drill_counter(image->drill_stats->drill_list,
289  state->current_tool);
290 
291  curr_net->next = g_new0(gerbv_net_t, 1);
292  if (curr_net->next == NULL) {
293  GERB_FATAL_ERROR("malloc curr_net->next failed in %s()",
294  __FUNCTION__);
295  }
296 
297  curr_net = curr_net->next;
298  curr_net->layer = image->layers;
299  curr_net->state = image->states;
300  curr_net->start_x = state->curr_x;
301  curr_net->start_y = state->curr_y;
302  /* KLUDGE. This function isn't allowed to return anything
303  but inches */
304  if(state->unit == GERBV_UNIT_MM) {
305  curr_net->start_x /= 25.4;
306  curr_net->start_y /= 25.4;
307  /* KLUDGE. All images, regardless of input format,
308  are returned in INCH format */
309  curr_net->state->unit = GERBV_UNIT_INCH;
310  }
311 
312  curr_net->stop_x = curr_net->start_x - state->origin_x;
313  curr_net->stop_y = curr_net->start_y - state->origin_y;
314  curr_net->aperture = state->current_tool;
316 
317  /* Check if aperture is set. Ignore the below instead of
318  causing SEGV... */
319  if(image->aperture[state->current_tool] == NULL) {
320  return curr_net;
321  }
322 
323  bbox = &curr_net->boundingBox;
324  r = image->aperture[state->current_tool]->parameter[0] / 2;
325 
326  /* Set boundingBox */
327  bbox->left = curr_net->start_x - r;
328  bbox->right = curr_net->start_x + r;
329  bbox->bottom = curr_net->start_y - r;
330  bbox->top = curr_net->start_y + r;
331 
332  drill_update_image_info_min_max_from_bbox(image->info, bbox);
333 
334  return curr_net;
335 }
336 
337 /*
338  * Adds a routed line segment (G00/G01 with tool down) to the drawing.
339  * Similar to drill_add_drill_hole but creates a line (APERTURE_STATE_ON)
340  * instead of a flash, with start and stop coordinates.
341  */
342 static gerbv_net_t *
343 drill_add_route_segment(gerbv_image_t *image, drill_state_t *state,
344  gerbv_drill_stats_t *stats, gerbv_net_t *curr_net,
345  double prev_x, double prev_y)
346 {
347  gerbv_render_size_t *bbox;
348  double r;
349  double start_x, start_y, stop_x, stop_y;
350 
351  curr_net->next = g_new0(gerbv_net_t, 1);
352  if (curr_net->next == NULL) {
353  GERB_FATAL_ERROR("malloc curr_net->next failed in %s()",
354  __FUNCTION__);
355  }
356 
357  curr_net = curr_net->next;
358  curr_net->layer = image->layers;
359  curr_net->state = image->states;
360 
361  start_x = prev_x;
362  start_y = prev_y;
363  stop_x = state->curr_x;
364  stop_y = state->curr_y;
365 
366  if (state->unit == GERBV_UNIT_MM) {
367  /* Convert to inches -- internal units */
368  start_x /= 25.4;
369  start_y /= 25.4;
370  stop_x /= 25.4;
371  stop_y /= 25.4;
372  curr_net->state->unit = GERBV_UNIT_INCH;
373  }
374 
375  curr_net->start_x = start_x;
376  curr_net->start_y = start_y;
377  curr_net->stop_x = stop_x;
378  curr_net->stop_y = stop_y;
379  curr_net->aperture = state->current_tool;
382 
383  /* Check if aperture is set. Ignore the below instead of
384  causing SEGV... */
385  if (image->aperture[state->current_tool] == NULL) {
386  return curr_net;
387  }
388 
389  bbox = &curr_net->boundingBox;
390  r = image->aperture[state->current_tool]->parameter[0] / 2;
391 
392  /* Set boundingBox covering both endpoints + tool radius */
393  bbox->left = MIN(start_x, stop_x) - r;
394  bbox->right = MAX(start_x, stop_x) + r;
395  bbox->bottom = MIN(start_y, stop_y) - r;
396  bbox->top = MAX(start_y, stop_y) + r;
397 
398  drill_update_image_info_min_max_from_bbox(image->info, bbox);
399 
400  return curr_net;
401 }
402 
403 /*
404  * Adds a routed arc segment (G02/G03 with tool down) to the drawing.
405  */
406 static gerbv_net_t *
407 drill_add_arc_segment(gerbv_image_t *image, drill_state_t *state,
408  gerbv_drill_stats_t *stats, gerbv_net_t *curr_net,
409  double prev_x, double prev_y)
410 {
411  gerbv_render_size_t *bbox;
412  double r;
413  double start_x, start_y, stop_x, stop_y;
414  double delta_cp_x, delta_cp_y;
415  int cw;
416 
417  curr_net->next = g_new0(gerbv_net_t, 1);
418  if (curr_net->next == NULL)
419  GERB_FATAL_ERROR("malloc curr_net->next failed in %s()",
420  __FUNCTION__);
421 
422  curr_net = curr_net->next;
423  curr_net->layer = image->layers;
424  curr_net->state = image->states;
425 
426  start_x = prev_x;
427  start_y = prev_y;
428  stop_x = state->curr_x;
429  stop_y = state->curr_y;
430  delta_cp_x = state->delta_cp_x;
431  delta_cp_y = state->delta_cp_y;
432 
433  if (state->unit == GERBV_UNIT_MM) {
434  /* Convert to inches -- internal units */
435  start_x /= 25.4;
436  start_y /= 25.4;
437  stop_x /= 25.4;
438  stop_y /= 25.4;
439  delta_cp_x /= 25.4;
440  delta_cp_y /= 25.4;
441  curr_net->state->unit = GERBV_UNIT_INCH;
442  }
443 
444  cw = (state->route_mode == DRILL_G_CWMOVE);
445 
446  if (state->found_arc_radius) {
447  /* A-parameter arc: convert radius to center offsets.
448  * Given start (sx,sy), end (ex,ey), radius r, direction CW/CCW,
449  * solve for the arc center and derive delta_cp from it. */
450  double radius = state->arc_radius;
451  double dx, dy, d, h, mx, my, px, py, sign, cp_x, cp_y;
452 
453  if (state->unit == GERBV_UNIT_MM)
454  radius /= 25.4;
455 
456  dx = stop_x - start_x;
457  dy = stop_y - start_y;
458  d = hypot(dx, dy);
459 
460  if (d < DBL_EPSILON) {
461  /* Start == end: full circle. Center is offset by radius
462  * perpendicular to the X axis (arbitrary choice). */
463  delta_cp_x = 0;
464  delta_cp_y = fabs(radius);
465  } else if (d > 2.0 * fabs(radius)) {
466  /* Chord longer than diameter — clamp radius to minimum.
467  * This avoids sqrt of a negative number while still
468  * producing a reasonable (semicircle) arc. */
469  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
470  _("Arc radius %.4f too small for chord %.4f; "
471  "clamping to semicircle"),
472  fabs(radius), d);
473  delta_cp_x = dx / 2.0;
474  delta_cp_y = dy / 2.0;
475  } else {
476  h = sqrt(radius * radius - (d / 2.0) * (d / 2.0));
477  mx = (start_x + stop_x) / 2.0;
478  my = (start_y + stop_y) / 2.0;
479 
480  /* Perpendicular unit vector to the chord */
481  px = -dy / d;
482  py = dx / d;
483 
484  /* CW arc: center is to the right of start->end vector.
485  * CCW arc: center is to the left. */
486  sign = cw ? -1.0 : 1.0;
487 
488  cp_x = mx + sign * h * px;
489  cp_y = my + sign * h * py;
490 
491  delta_cp_x = cp_x - start_x;
492  delta_cp_y = cp_y - start_y;
493  }
494 
495  state->found_arc_radius = FALSE;
496  }
497 
498  curr_net->start_x = start_x;
499  curr_net->start_y = start_y;
500  curr_net->stop_x = stop_x;
501  curr_net->stop_y = stop_y;
502  curr_net->aperture = state->current_tool;
504 
507 
508  curr_net->cirseg = g_new0(gerbv_cirseg_t, 1);
509  if (curr_net->cirseg == NULL)
510  GERB_FATAL_ERROR("malloc cirseg failed in %s()", __FUNCTION__);
511 
512  calc_cirseg_mq(curr_net, cw, delta_cp_x, delta_cp_y);
513 
514  /* Check if aperture is set. Skip bbox computation if not. */
515  if (image->aperture[state->current_tool] == NULL)
516  return curr_net;
517 
518  /* Compute bounding box from arc geometry */
519  bbox = &curr_net->boundingBox;
520  r = image->aperture[state->current_tool]->parameter[0] / 2;
521 
522  {
523  double ang1, ang2, step_pi_2, x, y;
524 
525  ang1 = DEG2RAD(MIN(curr_net->cirseg->angle1,
526  curr_net->cirseg->angle2));
527  ang2 = DEG2RAD(MAX(curr_net->cirseg->angle1,
528  curr_net->cirseg->angle2));
529 
530  /* Start arc point */
531  x = curr_net->cirseg->cp_x +
532  curr_net->cirseg->width * cos(ang1) / 2;
533  y = curr_net->cirseg->cp_y +
534  curr_net->cirseg->width * sin(ang1) / 2;
535  bbox->left = x - r;
536  bbox->right = x + r;
537  bbox->bottom = y - r;
538  bbox->top = y + r;
539 
540  /* Middle arc points at each 90-degree axis crossing */
541  for (step_pi_2 = (floor(ang1 / M_PI_2) + 1) * M_PI_2;
542  step_pi_2 < MIN(ang2, ang1 + 2 * M_PI);
543  step_pi_2 += M_PI_2) {
544  x = curr_net->cirseg->cp_x +
545  curr_net->cirseg->width * cos(step_pi_2) / 2;
546  y = curr_net->cirseg->cp_y +
547  curr_net->cirseg->width * sin(step_pi_2) / 2;
548  bbox->left = MIN(bbox->left, x - r);
549  bbox->right = MAX(bbox->right, x + r);
550  bbox->bottom = MIN(bbox->bottom, y - r);
551  bbox->top = MAX(bbox->top, y + r);
552  }
553 
554  /* Stop arc point */
555  x = curr_net->cirseg->cp_x +
556  curr_net->cirseg->width * cos(ang2) / 2;
557  y = curr_net->cirseg->cp_y +
558  curr_net->cirseg->width * sin(ang2) / 2;
559  bbox->left = MIN(bbox->left, x - r);
560  bbox->right = MAX(bbox->right, x + r);
561  bbox->bottom = MIN(bbox->bottom, y - r);
562  bbox->top = MAX(bbox->top, y + r);
563  }
564 
565  drill_update_image_info_min_max_from_bbox(image->info, bbox);
566 
567  return curr_net;
568 }
569 
570 /* -------------------------------------------------------------- */
572 parse_drillfile(gerb_file_t *fd, gerbv_HID_Attribute *attr_list, int n_attr, int reload)
573 {
574  drill_state_t *state = NULL;
575  gerbv_image_t *image = NULL;
576  gerbv_net_t *curr_net = NULL;
577  gerbv_HID_Attribute *hid_attrs;
578  int read;
579  gboolean parsing_done = FALSE;
580  gerbv_drill_stats_t *stats;
581  gchar *tmps;
582  unsigned int file_line = 1;
583 
584  /*
585  * many locales redefine "." as "," and so on, so sscanf and strtod
586  * has problems when reading files using %f format.
587  * Fixes bug #1963618 reported by Lorenzo Marcantonio.
588  */
589  setlocale(LC_NUMERIC, "C" );
590 
591  /* Create new image for this layer */
592  DPRINTF("In parse_drillfile, about to create image for this layer\n");
593 
594  image = gerbv_create_image(image, "Excellon Drill File");
595  if (image == NULL) {
596  GERB_FATAL_ERROR("malloc image failed in %s()", __FUNCTION__);
597  }
598 
599  if (reload && attr_list != NULL) {
600  /* FIXME there should probably just be a function to copy an
601  attribute list including using strdup as needed */
602 
603  image->info->n_attr = n_attr;
604  image->info->attr_list = gerbv_attribute_dup(attr_list, n_attr);
605 
606  } else {
607  /* Copy in the default attribute list for drill files. We make a
608  * copy here because we will allow per-layer editing of the
609  * attributes.
610  */
611  image->info->n_attr = sizeof (drill_attribute_list) / sizeof (drill_attribute_list[0]);
612  image->info->attr_list = gerbv_attribute_dup (drill_attribute_list, image->info->n_attr);
613 
614  /* now merge any project attributes */
615  drill_attribute_merge (image->info->attr_list, image->info->n_attr,
616  attr_list, n_attr);
617  }
618 
619  curr_net = image->netlist;
620  curr_net->layer = image->layers;
621  curr_net->state = image->states;
623  stats = gerbv_drill_stats_new();
624  if (stats == NULL) {
625  GERB_FATAL_ERROR("malloc stats failed in %s()", __FUNCTION__);
626  }
627  image->drill_stats = stats;
628 
629  /* Create local state variable to track photoplotter state */
630  state = new_state(state);
631  if (state == NULL) {
632  GERB_FATAL_ERROR("malloc state failed in %s()", __FUNCTION__);
633  }
634 
635  image->format = g_new0(gerbv_format_t, 1);
636  if (image->format == NULL) {
637  GERB_FATAL_ERROR("malloc format failed in %s()", __FUNCTION__);
638  }
639 
640  image->format->omit_zeros = GERBV_OMIT_ZEROS_UNSPECIFIED;
641 
642  hid_attrs = image->info->attr_list;
643 
644  if (!hid_attrs[HA_auto].default_val.int_value) {
645  state->autod = 0;
646  state->number_format = FMT_USER;
647  state->decimals = hid_attrs[HA_digits].default_val.int_value;
648 
649  if (GERBV_UNIT_MM == hid_attrs[HA_xy_units].default_val.int_value) {
650  state->unit = GERBV_UNIT_MM;
651  }
652 
653  switch (hid_attrs[HA_suppression].default_val.int_value) {
654  case SUP_LEAD:
655  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
656  break;
657 
658  case SUP_TRAIL:
659  image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
660  break;
661 
662  default:
663  image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
664  break;
665  }
666  }
667 
668  DPRINTF("%s(): Starting parsing of drill file \"%s\"\n",
669  __FUNCTION__, fd->filename);
670 
671  while (!parsing_done && (read = gerb_fgetc(fd)) != EOF) {
672 
673  switch ((char) read) {
674 
675  case ';' :
676  /* Comment found. Eat rest of line */
677  if (drill_parse_header_is_metric_comment(fd, state, image, file_line)) {
678  break;
679  }
680  tmps = get_line(fd);
681  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
682  _("Comment \"%s\" at line %u in file \"%s\""),
683  tmps, file_line, fd->filename);
684  DPRINTF(" Comment with ';' \"%s\" at line %u\n",
685  tmps, file_line);
686  g_free(tmps);
687  break;
688 
689  case 'A' :
690  /* ATC,ON/OFF — automatic tool change. Machine-only command
691  * found in Zuken CR-8000 Excellon output. Silently ignored,
692  * similar to DETECT,ON/OFF. See issue #93. */
693  gerb_ungetc(fd);
694  tmps = get_line(fd);
695  if (strcmp(tmps, "ATC,ON") == 0 ||
696  strcmp(tmps, "ATC,OFF") == 0) {
697  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
698  _("Ignoring ATC command \"%s\" "
699  "at line %u in file \"%s\""),
700  tmps, file_line, fd->filename);
701  } else {
702  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
703  _("Undefined code \"%s\" "
704  "at line %u in file \"%s\""),
705  tmps, file_line, fd->filename);
706  }
707  g_free(tmps);
708  break;
709 
710  case 'D' :
711  gerb_ungetc (fd);
712  tmps = get_line (fd);
713  if (strcmp (tmps, "DETECT,ON") == 0 ||
714  strcmp (tmps, "DETECT,OFF") == 0) {
715  gchar *tmps2;
716  gchar *tmps3;
717  if (strcmp (tmps, "DETECT,ON") == 0) {
718  tmps3 = "ON";
719  } else {
720  tmps3 = "OFF";
721  }
722 
723  /* broken tool detect on/off. Silently ignored. */
724  if (stats->detect) {
725  tmps2 = g_strdup_printf ("%s\n%s", stats->detect, tmps3);
726  g_free (stats->detect);
727  } else {
728  tmps2 = g_strdup_printf ("%s", tmps3);
729  }
730  stats->detect = tmps2;
731  } else {
732  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
733  _("Unrecognised string \"%s\" in header "
734  "at line %u in file \"%s\""),
735  tmps, file_line, fd->filename);
736  }
737  g_free (tmps);
738  break;
739 
740  case 'F' :
741  gerb_ungetc (fd);
742  tmps = get_line (fd);
743 
744  /* Silently ignore FMAT,2. Not sure what others are allowed */
745  if (0 == strcmp (tmps, "FMAT,2")) {
746  g_free (tmps);
747  break;
748  }
749 
750  if (0 == strcmp (tmps, "FMAT,1")) {
751  gerbv_stats_printf(stats->error_list,
753  _("File in unsupported format 1 "
754  "at line %u in file \"%s\""),
755  file_line, fd->filename);
756 
757  gerbv_destroy_image(image);
758  g_free (tmps);
759 
760  return NULL;
761  }
762 
763  gerbv_stats_printf(stats->error_list,
765  _("Unrecognised string \"%s\" in header "
766  "at line %u in file \"%s\""),
767  tmps, file_line, fd->filename);
768 
769  g_free (tmps);
770  break;
771 
772  case 'G': {
773  /* Most G codes aren't used, for now */
774  drill_g_code_t g_code;
775 
776  switch (g_code = drill_parse_G_code(fd, image, file_line)) {
777 
778  case DRILL_G_DRILL :
779  /* Drill mode - reset routing state */
780  state->route_mode = DRILL_G_DRILL;
781  state->tool_down = FALSE;
782  break;
783 
784  case DRILL_G_ROUT : /* G00 - rapid positioning */
785  state->route_mode = DRILL_G_ROUT;
786  break;
787  case DRILL_G_LINEARMOVE : /* G01 - linear routing */
788  state->route_mode = DRILL_G_LINEARMOVE;
789  break;
790  case DRILL_G_CWMOVE : /* G02 - CW arc routing */
791  state->route_mode = DRILL_G_CWMOVE;
792  break;
793  case DRILL_G_CCWMOVE : /* G03 - CCW arc routing */
794  state->route_mode = DRILL_G_CCWMOVE;
795  break;
796 
797  case DRILL_G_SLOT : {
798  /* Parse drilled slot end coords */
799  gerbv_render_size_t *bbox = &curr_net->boundingBox;
800  double r;
801 
802  if (EOF == (read = gerb_fgetc(fd))) {
803  gerbv_stats_printf(stats->error_list,
805  _("Unexpected EOF found in file \"%s\""),
806  fd->filename);
807  break;
808  }
809 
810  drill_parse_coordinate(fd, read, image, state, file_line);
811 
812  /* Modify last curr_net as drilled slot */
813  curr_net->stop_x = state->curr_x;
814  curr_net->stop_y = state->curr_y;
815 
816  if (state->unit == GERBV_UNIT_MM) {
817  /* Convert to inches -- internal units */
818  curr_net->stop_x /= 25.4;
819  curr_net->stop_y /= 25.4;
820  }
821 
822  r = image->aperture[state->current_tool]->parameter[0]/2;
823 
824  /* Update boundingBox with drilled slot stop_x,y coords */
825  bbox->left = MIN(bbox->left, curr_net->stop_x - r);
826  bbox->right = MAX(bbox->right, curr_net->stop_x + r);
827  bbox->bottom = MIN(bbox->bottom, curr_net->stop_y - r);
828  bbox->top = MAX(bbox->top, curr_net->stop_y + r);
829 
830  drill_update_image_info_min_max_from_bbox(image->info, bbox);
831 
833 
834  break;
835  }
836 
837  case DRILL_G_ROUTSLOT : {
838  /* G87 routed slot: read end XY coordinate, then create
839  * a routed line segment from current position to end */
840  double prev_x = state->curr_x;
841  double prev_y = state->curr_y;
842 
843  if (EOF == (read = gerb_fgetc(fd))) {
844  gerbv_stats_printf(stats->error_list,
846  _("Unexpected EOF found in file \"%s\""),
847  fd->filename);
848  break;
849  }
850 
851  drill_parse_coordinate(fd, read, image, state, file_line);
852 
853  curr_net = drill_add_route_segment(image, state, stats,
854  curr_net, prev_x, prev_y);
855  break;
856  }
857 
858  case DRILL_G_ABSOLUTE :
859  state->coordinate_mode = DRILL_MODE_ABSOLUTE;
860  break;
861 
862  case DRILL_G_INCREMENTAL :
863  state->coordinate_mode = DRILL_MODE_INCREMENTAL;
864  break;
865 
866  case DRILL_G_ZEROSET :
867  if (EOF == (read = gerb_fgetc(fd))) {
868  gerbv_stats_printf(stats->error_list,
870  _("Unexpected EOF found in file \"%s\""),
871  fd->filename);
872  break;
873  }
874 
875  drill_parse_coordinate(fd, (char)read, image,
876  state, file_line);
877  state->origin_x = state->curr_x;
878  state->origin_y = state->curr_y;
879  break;
880 
881  case DRILL_G_UNKNOWN:
882  tmps = get_line(fd);
883  gerbv_stats_printf(stats->error_list,
885  _("Unrecognized string \"%s\" found "
886  "at line %u in file \"%s\""),
887  tmps, file_line, fd->filename);
888  g_free(tmps);
889  break;
890 
891  case DRILL_G_OVERRIDETOOLSPEED: /* G07 */
892  case DRILL_G_VISTOOL: /* G34 */
893  case DRILL_G_VISSINGLEPOINTOFFSET: /* G35 */
894  case DRILL_G_VISMULTIPOINTTRANS: /* G36 */
895  case DRILL_G_VISCANCEL: /* G37 */
896  case DRILL_G_VISCORRHOLEDRILL: /* G38 */
897  case DRILL_G_VISAUTOCALIBRATION: /* G39 */
898  case DRILL_G_CUTTERCOMPOFF: /* G40 */
899  case DRILL_G_CUTTERCOMPLEFT: /* G41 */
900  case DRILL_G_CUTTERCOMPRIGHT: /* G42 */
901  case DRILL_G_VISSINGLEPOINTOFFSETREL: /* G45 */
902  case DRILL_G_VISMULTIPOINTTRANSREL: /* G46 */
903  case DRILL_G_VISCANCELREL: /* G47 */
904  case DRILL_G_VISCORRHOLEDRILLREL: /* G48 */
905  case DRILL_G_PACKDIP2: /* G81 */
906  case DRILL_G_PACKDIP: /* G82 */
907  case DRILL_G_PACK8PINL: /* G83 */
908  case DRILL_G_CIRLE: /* G84 */
909  eat_line(fd);
910  gerbv_stats_printf(stats->error_list,
911  GERBV_MESSAGE_NOTE, -1,
912  _("Ignoring machine-only G%02d (%s) "
913  "at line %u in file \"%s\""),
914  g_code, _(drill_g_code_name(g_code)),
915  file_line, fd->filename);
916  break;
917 
918  default:
919  eat_line(fd);
920  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
921  _("Unsupported G%02d (%s) code "
922  "at line %u in file \"%s\""),
923  g_code, drill_g_code_name(g_code),
924  file_line, fd->filename);
925  break;
926  }
927 
928  break;
929  }
930 
931  case 'I':
932  gerb_ungetc(fd); /* To compare full string in function or
933  report full string */
934  if (drill_parse_header_is_inch(fd, state, image, file_line)) {
935  break;
936  }
937 
938  if (drill_parse_header_is_ici(fd, state, image, file_line)) {
939  break;
940  }
941 
942  tmps = get_line(fd);
943  gerbv_stats_printf(stats->error_list,
945  _("Unrecognized string \"%s\" found "
946  "at line %u in file \"%s\""),
947  tmps, file_line, fd->filename);
948  g_free(tmps);
949 
950  break;
951 
952  case 'M': {
953  int m_code;
954 
955  switch (m_code = drill_parse_M_code(fd, state, image, file_line)) {
956  case DRILL_M_HEADER :
957  state->curr_section = DRILL_HEADER;
958  break;
959  case DRILL_M_HEADEREND :
960  state->curr_section = DRILL_DATA;
961 
962  if (image->format->omit_zeros == GERBV_OMIT_ZEROS_UNSPECIFIED) {
963  /* Excellon says they default to specify leading
964  zeros, i.e. omit trailing zeros. The Excellon
965  files floating around that don't specify the
966  leading/trailing zeros in the header seem to
967  contradict to this though.
968 
969  XXX We should probably ask the user. */
970 
971  gerbv_stats_printf(stats->error_list,
973  _("End of Excellon header reached "
974  "but no leading/trailing zero "
975  "handling specified "
976  "at line %u in file \"%s\""),
977  file_line, fd->filename);
978  gerbv_stats_printf(stats->error_list,
980  _("Assuming leading zeros in file \"%s\""),
981  fd->filename);
982  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
983  }
984  break;
985  case DRILL_M_METRIC :
986  if (state->unit == GERBV_UNIT_UNSPECIFIED
987  && state->curr_section != DRILL_HEADER) {
988  double size;
989 
990  gerbv_stats_printf(stats->error_list,
992  _("M71 code found but no METRIC "
993  "specification in header "
994  "at line %u in file \"%s\""),
995  file_line, fd->filename);
996  gerbv_stats_printf(stats->error_list,
998  _("Assuming all tool sizes are MM in file \"%s\""),
999  fd->filename);
1000 
1001  stats = image->drill_stats;
1002  for (int tool_num = TOOL_MIN; tool_num < TOOL_MAX;
1003  tool_num++) {
1004  if (image->aperture[tool_num]) {
1005  /* First update stats. Do this before changing drill dias.
1006  * Maybe also put error into stats? */
1007  size = image->aperture[tool_num]->parameter[0];
1008  drill_stats_modify_drill_list(stats->drill_list,
1009  tool_num,
1010  size,
1011  "MM");
1012  /* Now go back and update all tool dias, since
1013  * tools are displayed in inch units
1014  */
1015  image->aperture[tool_num]->parameter[0] /= 25.4;
1016  }
1017  }
1018  }
1019  if (state->autod) {
1020  state->number_format = state->backup_number_format;
1021  state->unit = GERBV_UNIT_MM;
1022  }
1023  break;
1024  case DRILL_M_IMPERIAL :
1025  if (state->autod) {
1026  if (state->number_format != FMT_00_0000) {
1027  /* save metric format definition for later */
1028  state->backup_number_format = state->number_format;
1029  }
1030  state->number_format = FMT_00_0000;
1031  state->decimals = 4;
1032  state->unit = GERBV_UNIT_INCH;
1033  }
1034 
1035  break;
1036  case DRILL_M_CANNEDTEXTX :
1037  case DRILL_M_CANNEDTEXTY :
1038  tmps = get_line(fd);
1039  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1040  _("Canned text \"%s\" "
1041  "at line %u in drill file \"%s\""),
1042  tmps, file_line, fd->filename);
1043  g_free(tmps);
1044  break;
1045  case DRILL_M_MESSAGELONG :
1046  case DRILL_M_MESSAGE :
1047  tmps = get_line(fd);
1048  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1049  _("Message \"%s\" embedded "
1050  "at line %u in drill file \"%s\""),
1051  tmps, file_line, fd->filename);
1052  g_free(tmps);
1053  break;
1054  case DRILL_M_PATTERN: /* M25 — begin pattern recording */
1055  state->in_pattern = TRUE;
1056  if (state->pattern_buffer) {
1057  g_array_set_size(state->pattern_buffer, 0);
1058  } else {
1059  state->pattern_buffer = g_array_new(FALSE, FALSE,
1060  sizeof(drill_pattern_entry_t));
1061  }
1062  break;
1063 
1064  case DRILL_M_PATTERNEND: /* M01 — end pattern recording */
1065  state->in_pattern = FALSE;
1066  break;
1067 
1068  case DRILL_M_REPEATPATTERNOFFSET: { /* M02 — repeat pattern at offset */
1069  double offset_x = 0.0, offset_y = 0.0;
1070  int c, saved_tool;
1071 
1072  /* Parse X/Y offsets (same format as R-code step) */
1073  c = gerb_fgetc(fd);
1074  if (c == 'X') {
1075  offset_x = read_double(fd, state->number_format,
1076  image->format->omit_zeros, state->decimals);
1077  c = gerb_fgetc(fd);
1078  }
1079  if (c == 'Y') {
1080  offset_y = read_double(fd, state->number_format,
1081  image->format->omit_zeros, state->decimals);
1082  } else {
1083  gerb_ungetc(fd);
1084  }
1085 
1086  /* Replay buffered drills at offset */
1087  if (state->pattern_buffer && state->pattern_buffer->len > 0) {
1088  double save_x = state->curr_x;
1089  double save_y = state->curr_y;
1090  saved_tool = state->current_tool;
1091 
1092  for (guint i = 0; i < state->pattern_buffer->len; i++) {
1093  drill_pattern_entry_t *e = &g_array_index(
1094  state->pattern_buffer,
1095  drill_pattern_entry_t, i);
1096  state->current_tool = e->tool;
1097  state->curr_x = e->x + offset_x;
1098  state->curr_y = e->y + offset_y;
1099 
1100  if (e->is_route) {
1101  curr_net = drill_add_route_segment(image, state,
1102  stats, curr_net,
1103  e->prev_x + offset_x,
1104  e->prev_y + offset_y);
1105  } else {
1106  curr_net = drill_add_drill_hole(image, state,
1107  stats, curr_net);
1108  }
1109  }
1110 
1111  state->curr_x = save_x;
1112  state->curr_y = save_y;
1113  state->current_tool = saved_tool;
1114  }
1115  break;
1116  }
1117 
1118  case DRILL_M_TOOLTIPCHECK:
1119  break;
1120 
1121  case DRILL_M_ZAXISROUTEPOSITIONDEPTHCTRL: /* M14 */
1122  case DRILL_M_ZAXISROUTEPOSITION: /* M15 */
1123  state->tool_down = TRUE;
1124  break;
1125  case DRILL_M_RETRACTCLAMPING: /* M16 */
1126  case DRILL_M_RETRACTNOCLAMPING: /* M17 */
1127  state->tool_down = FALSE;
1128  break;
1129 
1130  case DRILL_M_SWAPAXIS: /* M70 */
1131  state->swap_axis = !state->swap_axis;
1132  break;
1133  case DRILL_M_MIRRORX: /* M80 */
1134  state->mirror_x = !state->mirror_x;
1135  break;
1136  case DRILL_M_MIRRORY: /* M90 */
1137  state->mirror_y = !state->mirror_y;
1138  break;
1139 
1140  case DRILL_M_END :
1141  /* M00 has optional arguments */
1142  eat_line(fd);
1143  /* M00 ends the program the same way as M30 */
1144  [[fallthrough]];
1145 
1146  case DRILL_M_ENDREWIND :
1147  parsing_done = TRUE;
1148  break;
1149 
1150  case DRILL_M_UNKNOWN:
1151  gerb_ungetc(fd); /* To compare full string in function or
1152  report full string */
1153  if (drill_parse_header_is_metric(fd, state, image, file_line)) {
1154  break;
1155  }
1156 
1157  stats->M_unknown++;
1158  tmps = get_line(fd);
1159  gerbv_stats_printf(stats->error_list,
1160  GERBV_MESSAGE_ERROR, -1,
1161  _("Unrecognized string \"%s\" found "
1162  "at line %u in file \"%s\""),
1163  tmps, file_line, fd->filename);
1164  g_free(tmps);
1165 
1166  break;
1167 
1168  case DRILL_M_STOPOPTIONAL: /* M06 */
1169  case DRILL_M_SANDREND: /* M08 */
1170  case DRILL_M_STOPINSPECTION: /* M09 */
1171  case DRILL_M_VISANDRPATTERN: /* M50 */
1172  case DRILL_M_VISANDRPATTERNREWIND: /* M51 */
1173  case DRILL_M_VISANDRPATTERNOFFSETCOUNTERCTRL: /* M52 */
1174  case DRILL_M_REFSCALING: /* M60 */
1175  case DRILL_M_REFSCALINGEND: /* M61 */
1176  case DRILL_M_PECKDRILLING: /* M62 */
1177  case DRILL_M_PECKDRILLINGEND: /* M63 */
1178  eat_line(fd);
1179  gerbv_stats_printf(stats->error_list,
1180  GERBV_MESSAGE_NOTE, -1,
1181  _("Ignoring machine-only M%02d (%s) "
1182  "at line %u in file \"%s\""),
1183  m_code, _(drill_m_code_name(m_code)),
1184  file_line, fd->filename);
1185  break;
1186 
1187  default:
1188  stats->M_unknown++;
1189  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1190  _("Unsupported M%02d (%s) code found "
1191  "at line %u in file \"%s\""),
1192  m_code, _(drill_m_code_name(m_code)),
1193  file_line, fd->filename);
1194  break;
1195  } /* switch(m_code) */
1196 
1197  break;
1198  } /* case 'M' */
1199 
1200  case 'R':
1201  if (state->curr_section == DRILL_HEADER) {
1202  stats->unknown++;
1203  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1204  _("Not allowed 'R' code in the header "
1205  "at line %u in file \"%s\""),
1206  file_line, fd->filename);
1207  } else {
1208  double start_x, start_y;
1209  double step_x, step_y;
1210  int c;
1211  int rcnt;
1212  /*
1213  * This is the "Repeat hole" command. Format is:
1214  * R##[X##][Y##]
1215  * This repeats the previous hole stepping in the X and
1216  * Y increments give. Example:
1217  * R04X0.1 -- repeats the drill hole 4 times, stepping
1218  * 0.1" in the X direction. Note that the X and Y step
1219  * sizes default to zero if not given and that they use
1220  * the same format and units as the normal X,Y
1221  * coordinates.
1222  */
1223  stats->R++;
1224 
1225  start_x = state->curr_x;
1226  start_y = state->curr_y;
1227 
1228  /* figure out how many repeats there are */
1229  c = gerb_fgetc (fd);
1230  rcnt = 0;
1231  while ( '0' <= c && c <= '9') {
1232  rcnt = 10*rcnt + (c - '0');
1233  c = gerb_fgetc (fd);
1234  }
1235  DPRINTF("working on R code (repeat) with a number of reps equal to %d\n", rcnt);
1236 
1237  step_x = 0.0;
1238  if (c == 'X') {
1239  step_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1240  c = gerb_fgetc (fd);
1241  }
1242 
1243  step_y = 0.0;
1244  if( c == 'Y') {
1245  step_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1246  } else {
1247  gerb_ungetc (fd);
1248  }
1249 
1250  DPRINTF("Getting ready to repeat the drill %d times with delta_x = %g, delta_y = %g\n", rcnt, step_x, step_y);
1251 
1252  /* spit out the drills */
1253  for (c = 1 ; c <= rcnt ; c++) {
1254  state->curr_x = start_x + c*step_x;
1255  state->curr_y = start_y + c*step_y;
1256  DPRINTF(" Repeat #%d - new location is (%g, %g)\n", c, state->curr_x, state->curr_y);
1257  curr_net = drill_add_drill_hole (image, state, stats, curr_net);
1258  if (state->in_pattern) {
1259  drill_pattern_entry_t entry = {
1260  state->curr_x, state->curr_y,
1261  0, 0, state->current_tool, FALSE
1262  };
1263  g_array_append_val(state->pattern_buffer, entry);
1264  }
1265  }
1266 
1267  }
1268  break;
1269 
1270  case 'S':
1271  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1272  _("Ignoring setting spindle speed "
1273  "at line %u in drill file \"%s\""),
1274  file_line, fd->filename);
1275  eat_line(fd);
1276  break;
1277  case 'T':
1278  drill_parse_T_code(fd, state, image, file_line);
1279  state->route_mode = DRILL_G_DRILL;
1280  state->tool_down = FALSE;
1281  state->delta_cp_x = 0;
1282  state->delta_cp_y = 0;
1283  break;
1284  case 'V' :
1285  gerb_ungetc (fd);
1286  tmps = get_line (fd);
1287  /* Silently ignore VER,1. Not sure what others are allowed */
1288  if (0 != strcmp (tmps, "VER,1")) {
1289  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1290  _("Undefined string \"%s\" in header "
1291  "at line %u in file \"%s\""),
1292  tmps, file_line, fd->filename);
1293  }
1294  g_free (tmps);
1295  break;
1296 
1297  case 'X':
1298  case 'Y': {
1299  double prev_x = state->curr_x;
1300  double prev_y = state->curr_y;
1301 
1302  /* Parse the coordinate(s) */
1303  drill_parse_coordinate(fd, read, image, state, file_line);
1304 
1305  if ((state->route_mode == DRILL_G_ROUT ||
1306  state->route_mode == DRILL_G_LINEARMOVE ||
1307  state->route_mode == DRILL_G_CWMOVE ||
1308  state->route_mode == DRILL_G_CCWMOVE) && !state->tool_down) {
1309  /* Routing mode, tool up: reposition only, no geometry */
1310  state->delta_cp_x = 0;
1311  state->delta_cp_y = 0;
1312  state->found_arc_radius = FALSE;
1313  break;
1314  }
1315 
1316  if ((state->route_mode == DRILL_G_CWMOVE ||
1317  state->route_mode == DRILL_G_CCWMOVE) && state->tool_down) {
1318  /* Arc routing mode, tool down: create arc segment */
1319  curr_net = drill_add_arc_segment(image, state, stats,
1320  curr_net, prev_x, prev_y);
1321  state->delta_cp_x = 0;
1322  state->delta_cp_y = 0;
1323  state->found_arc_radius = FALSE;
1324  } else if ((state->route_mode == DRILL_G_LINEARMOVE ||
1325  state->route_mode == DRILL_G_ROUT) && state->tool_down) {
1326  /* Routing mode, tool down: create line segment */
1327  curr_net = drill_add_route_segment(image, state, stats,
1328  curr_net, prev_x, prev_y);
1329  if (state->in_pattern) {
1330  drill_pattern_entry_t entry = {
1331  state->curr_x, state->curr_y,
1332  prev_x, prev_y, state->current_tool, TRUE
1333  };
1334  g_array_append_val(state->pattern_buffer, entry);
1335  }
1336  } else {
1337  /* Drill mode (default): create flash hole */
1338  curr_net = drill_add_drill_hole(image, state, stats, curr_net);
1339  if (state->in_pattern) {
1340  drill_pattern_entry_t entry = {
1341  state->curr_x, state->curr_y,
1342  0, 0, state->current_tool, FALSE
1343  };
1344  g_array_append_val(state->pattern_buffer, entry);
1345  }
1346  }
1347  break;
1348  }
1349 
1350  case '%':
1351  state->curr_section = DRILL_DATA;
1352  break;
1353 
1354  case '\n' :
1355  file_line++;
1356 
1357  /* Get <CR> char, if any, from <LF><CR> pair */
1358  read = gerb_fgetc(fd);
1359  if (read != '\r' && read != EOF) {
1360  gerb_ungetc(fd);
1361  }
1362  break;
1363 
1364  case '\r' :
1365  file_line++;
1366 
1367  /* Get <LF> char, if any, from <CR><LF> pair */
1368  read = gerb_fgetc(fd);
1369  if (read != '\n' && read != EOF) {
1370  gerb_ungetc(fd);
1371  }
1372  break;
1373 
1374  case ' ' : /* White space */
1375  case '\t' :
1376  break;
1377 
1378  default:
1379  stats->unknown++;
1380 
1381  if (DRILL_HEADER == state->curr_section) {
1382  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1383  _("Undefined code '%s' (0x%x) found in header "
1384  "at line %u in file \"%s\""),
1385  gerbv_escape_char(read), read,
1386  file_line, fd->filename);
1387  gerb_ungetc(fd);
1388 
1389  /* Unrecognised crap in the header is thrown away */
1390  tmps = get_line(fd);
1391  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1392  _("Unrecognised string \"%s\" in header "
1393  "at line %u in file \"%s\""),
1394  tmps, file_line, fd->filename);
1395  g_free (tmps);
1396  } else {
1397  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1398  _("Ignoring undefined character '%s' (0x%x) "
1399  "found inside data at line %u in file \"%s\""),
1400  gerbv_escape_char(read), read, file_line, fd->filename);
1401  }
1402  }
1403  }
1404 
1405  if (!parsing_done) {
1406  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1407  _("No EOF found in drill file \"%s\""), fd->filename);
1408  }
1409 
1410  DPRINTF("%s(): Populating file attributes\n", __FUNCTION__);
1411 
1412  hid_attrs = image->info->attr_list;
1413 
1414  switch (state->unit) {
1415  case GERBV_UNIT_MM:
1416  hid_attrs[HA_xy_units].default_val.int_value = GERBV_UNIT_MM;
1417  break;
1418 
1419  default:
1420  hid_attrs[HA_xy_units].default_val.int_value = GERBV_UNIT_INCH;
1421  break;
1422  }
1423 
1424  switch (state->number_format) {
1425  case FMT_000_00:
1426  case FMT_0000_00:
1427  hid_attrs[HA_digits].default_val.int_value = 2;
1428  break;
1429 
1430  case FMT_000_000:
1431  hid_attrs[HA_digits].default_val.int_value = 3;
1432  break;
1433 
1434  case FMT_00_0000:
1435  hid_attrs[HA_digits].default_val.int_value = 4;
1436  break;
1437 
1438  case FMT_USER:
1439  DPRINTF("%s(): Keeping user specified number of decimal places (%d)\n",
1440  __FUNCTION__,
1441  hid_attrs[HA_digits].default_val.int_value);
1442  break;
1443 
1444  default:
1445  break;
1446  }
1447 
1448  switch (image->format->omit_zeros) {
1450  hid_attrs[HA_suppression].default_val.int_value = SUP_LEAD;
1451  break;
1452 
1454  hid_attrs[HA_suppression].default_val.int_value = SUP_TRAIL;
1455  break;
1456 
1457  default:
1458  hid_attrs[HA_suppression].default_val.int_value = SUP_NONE;
1459  break;
1460  }
1461 
1462  if (state->pattern_buffer)
1463  g_array_free(state->pattern_buffer, TRUE);
1464  g_free(state);
1465 
1466  return image;
1467 } /* parse_drillfile */
1468 
1469 
1470 /* -------------------------------------------------------------- */
1471 /*
1472  * Checks for signs that this is a drill file
1473  * Returns TRUE if it is, FALSE if not.
1474  */
1475 gboolean
1476 drill_file_p(gerb_file_t *fd, gboolean *returnFoundBinary)
1477 {
1478  char *buf=NULL, *tbuf;
1479  int len = 0;
1480  char *letter;
1481  int ascii;
1482  int zero = 48; /* ascii 0 */
1483  int nine = 57; /* ascii 9 */
1484  int i;
1485  gboolean found_binary = FALSE;
1486  gboolean found_M48 = FALSE;
1487  gboolean found_M30 = FALSE;
1488  gboolean found_percent = FALSE;
1489  gboolean found_T = FALSE;
1490  gboolean found_X = FALSE;
1491  gboolean found_Y = FALSE;
1492  gboolean end_comments=FALSE;
1493 
1494  tbuf = g_malloc(MAXL);
1495  if (tbuf == NULL) {
1496  GERB_FATAL_ERROR(
1497  "malloc buf failed while checking for drill file in %s()",
1498  __FUNCTION__);
1499  }
1500 
1501  while (fgets(tbuf, MAXL, fd->fd) != NULL) {
1502  len = strlen(tbuf);
1503  buf = tbuf;
1504  /* check for comments at top of file. */
1505  if(!end_comments){
1506  if(g_strstr_len(buf, len, ";")!=NULL){/* comments at top of file */
1507  for (i = 0; i < len-1; ++i) {
1508  if (buf[i] == '\n'
1509  && buf[i+1] != ';'
1510  && buf[i+1] != '\r'
1511  && buf[i+1] != '\n') {
1512  end_comments = TRUE;
1513  /* Set rest of parser to end of
1514  * comments */
1515  buf = &tbuf[i+1];
1516 
1517  }
1518  }
1519  if(!end_comments) {
1520  continue;
1521  }
1522  } else {
1523  end_comments=TRUE;
1524  }
1525  }
1526 
1527  /* First look through the file for indications of its type */
1528  len = strlen(buf);
1529  /* check that file is not binary (non-printing chars) */
1530  for (i = 0; i < len; i++) {
1531  ascii = (int) buf[i];
1532  if ((ascii > 128) || (ascii < 0)) {
1533  found_binary = TRUE;
1534  }
1535  }
1536 
1537  /* Check for M48 = start of drill header */
1538  if (g_strstr_len(buf, len, "M48")) {
1539  found_M48 = TRUE;
1540  }
1541 
1542  /* Check for M30 = end of drill program */
1543  if (g_strstr_len(buf, len, "M30")) {
1544  if (found_percent) {
1545  found_M30 = TRUE; /* Found M30 after % = good */
1546  }
1547  }
1548 
1549  /* Check for % on its own line at end of header */
1550  if ((letter = g_strstr_len(buf, len, "%")) != NULL) {
1551  if ((letter[1] == '\r') || (letter[1] == '\n')) {
1552  found_percent = TRUE;
1553  }
1554  }
1555 
1556  /* Check for T<number> */
1557  if ((letter = g_strstr_len(buf, len, "T")) != NULL) {
1558  if (!found_T && (found_X || found_Y)) {
1559  found_T = FALSE; /* Found first T after X or Y */
1560  } else {
1561  if (isdigit( (int) letter[1])) { /* verify next char is digit */
1562  found_T = TRUE;
1563  }
1564  }
1565  }
1566 
1567  /* look for X<number> or Y<number> */
1568  if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
1569  ascii = (int) letter[1]; /* grab char after X */
1570  if ((ascii >= zero) && (ascii <= nine)) {
1571  found_X = TRUE;
1572  }
1573  }
1574  if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
1575  ascii = (int) letter[1]; /* grab char after Y */
1576  if ((ascii >= zero) && (ascii <= nine)) {
1577  found_Y = TRUE;
1578  }
1579  }
1580  } /* while (fgets(buf, MAXL, fd->fd) */
1581 
1582  rewind(fd->fd);
1583  g_free(tbuf);
1584  *returnFoundBinary = found_binary;
1585 
1586  /* Now form logical expression determining if this is a drill file */
1587  if ( ((found_X || found_Y) && found_T) &&
1588  (found_M48 || (found_percent && found_M30)) ) {
1589  return TRUE;
1590  } else if (found_M48 && found_percent && found_M30) {
1591  /* Pathological case of drill file with valid header
1592  and EOF but no drill XY locations. */
1593  return TRUE;
1594  } else {
1595  return FALSE;
1596  }
1597 } /* drill_file_p */
1598 
1599 
1600 /* -------------------------------------------------------------- */
1601 /* Parse tool definition. This can get a bit tricky since it can
1602  appear in the header and/or data section.
1603  Returns tool number on success, -1 on error */
1604 static int
1605 drill_parse_T_code(gerb_file_t *fd, drill_state_t *state,
1606  gerbv_image_t *image, unsigned int file_line)
1607 {
1608  int tool_num;
1609  gboolean done = FALSE;
1610  int temp;
1611  double size;
1612  gerbv_drill_stats_t *stats = image->drill_stats;
1613  gerbv_aperture_t *apert;
1614  gchar *tmps;
1615  gchar *string;
1616 
1617  DPRINTF("---> entering %s()...\n", __FUNCTION__);
1618 
1619  /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
1620  broken headers from Orcad, which is crap */
1621  temp = gerb_fgetc(fd);
1622  DPRINTF(" Found a char '%s' (0x%02x) after the T\n",
1623  gerbv_escape_char(temp), temp);
1624 
1625  /* might be a tool tool change stop switch on/off*/
1626  if((temp == 'C') && ((fd->ptr + 2) < fd->datalen)){
1627  if(gerb_fgetc(fd) == 'S'){
1628  if (gerb_fgetc(fd) == 'T' ){
1629  fd->ptr -= 4;
1630  tmps = get_line(fd++);
1631  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_NOTE, -1,
1632  _("Tool change stop switch found \"%s\" "
1633  "at line %u in file \"%s\""),
1634  tmps, file_line, fd->filename);
1635  g_free (tmps);
1636 
1637  return -1;
1638  }
1639  gerb_ungetc(fd);
1640  }
1641  gerb_ungetc(fd);
1642  }
1643 
1644  if( !(isdigit(temp) != 0 || temp == '+' || temp =='-') ) {
1645  if(temp != EOF) {
1646  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1647  _("OrCAD bug: Junk text found in place of tool definition"));
1648  tmps = get_line(fd);
1649  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1650  _("Junk text \"%s\" "
1651  "at line %u in file \"%s\""),
1652  tmps, file_line, fd->filename);
1653  g_free (tmps);
1654  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1655  _("Ignoring junk text"));
1656  }
1657  return -1;
1658  }
1659  gerb_ungetc(fd);
1660 
1661  tool_num = (int) gerb_fgetint(fd, NULL);
1662  DPRINTF(" Handling tool T%d at line %u\n", tool_num, file_line);
1663 
1664  if (tool_num == 0) {
1665  /* T0 is nominally an unload-tool command, but some CAD tools
1666  (e.g. ekf2) define T0 with a C diameter parameter in the
1667  header. Consume any trailing parameters so they don't get
1668  mis-parsed as unknown header codes, then return. */
1669  temp = gerb_fgetc(fd);
1670  while (temp != EOF && temp != '\n' && temp != '\r') {
1671  if (temp == 'C') {
1672  read_double(fd, state->header_number_format,
1673  GERBV_OMIT_ZEROS_TRAILING, state->decimals);
1674  } else if (temp == 'F' || temp == 'S') {
1675  gerb_fgetint(fd, NULL);
1676  } else {
1677  gerb_ungetc(fd);
1678  break;
1679  }
1680  temp = gerb_fgetc(fd);
1681  }
1682  if (temp == '\n' || temp == '\r')
1683  gerb_ungetc(fd);
1684  return tool_num;
1685  }
1686 
1687  if (tool_num < TOOL_MIN || tool_num >= TOOL_MAX) {
1688  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1689  _("Out of bounds drill number %d "
1690  "at line %u in file \"%s\""),
1691  tool_num, file_line, fd->filename);
1692  return -1;
1693  }
1694 
1695  /* Set the current tool to the correct one */
1696  state->current_tool = tool_num;
1697  apert = image->aperture[tool_num];
1698 
1699  /* Check for a size definition */
1700  temp = gerb_fgetc(fd);
1701 
1702  /* This bit of code looks for a tool definition by scanning for strings
1703  * of form TxxC, TxxF, TxxS. */
1704  while (!done) {
1705  switch((char)temp) {
1706  case 'C':
1707  size = read_double(fd, state->header_number_format, GERBV_OMIT_ZEROS_TRAILING, state->decimals);
1708  DPRINTF(" Read a size of %g\n", size);
1709 
1710  if (state->unit == GERBV_UNIT_MM) {
1711  size /= 25.4;
1712  } else if(size >= 4.0) {
1713  /* If the drill size is >= 4 inches, assume that this
1714  must be wrong and that the units are mils.
1715  The limit being 4 inches is because the smallest drill
1716  I've ever seen used is 0,3mm(about 12mil). Half of that
1717  seemed a bit too small a margin, so a third it is */
1718 
1719  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1720  _("Read a drill of diameter %g inches "
1721  "at line %u in file \"%s\""),
1722  size, file_line, fd->filename);
1723  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1724  _("Assuming units are mils"));
1725  size /= 1000.0;
1726  }
1727 
1728  if (size <= 0. || size >= 10000.) {
1729  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1730  _("Unreasonable drill size %g found for drill %d "
1731  "at line %u in file \"%s\""),
1732  size, tool_num, file_line, fd->filename);
1733  } else {
1734  if (apert != NULL) {
1735  /* allow a redefine of a tool only if the new definition is exactly the same.
1736  * This avoid lots of spurious complaints with the output of some cad
1737  * tools while keeping complaints if there is a true problem
1738  */
1739  if (apert->parameter[0] != size
1740  || apert->type != GERBV_APTYPE_CIRCLE
1741  || apert->nuf_parameters != 1
1742  || apert->unit != GERBV_UNIT_INCH) {
1743 
1744  gerbv_stats_printf(stats->error_list,
1745  GERBV_MESSAGE_ERROR, -1,
1746  _("Found redefinition of drill %d "
1747  "at line %u in file \"%s\""),
1748  tool_num, file_line, fd->filename);
1749  }
1750  } else {
1751  apert = image->aperture[tool_num] =
1752  g_new0(gerbv_aperture_t, 1);
1753  if (apert == NULL) {
1754  GERB_FATAL_ERROR("malloc tool failed in %s()",
1755  __FUNCTION__);
1756  }
1757 
1758  /* There's really no way of knowing what unit the tools
1759  are defined in without sneaking a peek in the rest of
1760  the file first. That's done in drill_guess_format() */
1761  apert->parameter[0] = size;
1762  apert->type = GERBV_APTYPE_CIRCLE;
1763  apert->nuf_parameters = 1;
1764  apert->unit = GERBV_UNIT_INCH;
1765  }
1766  }
1767 
1768  /* Add the tool whose definition we just found into the list
1769  * of tools for this layer used to generate statistics. */
1770  stats = image->drill_stats;
1771  string = g_strdup_printf("%s", (state->unit == GERBV_UNIT_MM ? _("mm") : _("inch")));
1772  drill_stats_add_to_drill_list(stats->drill_list,
1773  tool_num,
1774  state->unit == GERBV_UNIT_MM ? size*25.4 : size,
1775  string);
1776  g_free(string);
1777  break;
1778 
1779  case 'F':
1780  case 'S' :
1781  /* Silently ignored. They're not important. */
1782  gerb_fgetint(fd, NULL);
1783  break;
1784 
1785  default:
1786  /* Stop when finding anything but what's expected
1787  (and put it back) */
1788  gerb_ungetc(fd);
1789  done = TRUE;
1790  break;
1791  } /* switch((char)temp) */
1792 
1793  temp = gerb_fgetc(fd);
1794  if (EOF == temp) {
1795  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1796  _("Unexpected EOF encountered in header of "
1797  "drill file \"%s\""), fd->filename);
1798 
1799  /* Restore new line character for processing */
1800  if ('\n' == temp || '\r' == temp) {
1801  gerb_ungetc(fd);
1802  }
1803  }
1804  } /* while(!done) */ /* Done looking at tool definitions */
1805 
1806  /* Catch the tools that aren't defined.
1807  This isn't strictly a good thing, but at least something is shown */
1808  if (apert == NULL) {
1809  double dia;
1810 
1811  apert = image->aperture[tool_num] = g_new0(gerbv_aperture_t, 1);
1812  if (apert == NULL) {
1813  GERB_FATAL_ERROR("malloc tool failed in %s()", __FUNCTION__);
1814  }
1815 
1816  /* See if we have the tool table */
1817  dia = gerbv_get_tool_diameter(tool_num);
1818  if (dia <= 0) {
1819  /*
1820  * There is no tool. So go out and make some.
1821  * This size calculation is, of course, totally bogus.
1822  */
1823  dia = (double)(16 + 8 * tool_num) / 1000;
1824  /*
1825  * Oooh, this is sooo ugly. But some CAD systems seem to always
1826  * use T00 at the end of the file while others that don't have
1827  * tool definitions inside the file never seem to use T00 at all.
1828  */
1829  if (tool_num != 0) {
1830  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1831  _("Tool %02d used without being defined "
1832  "at line %u in file \"%s\""),
1833  tool_num, file_line, fd->filename);
1834  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1835  _("Setting a default size of %g\""), dia);
1836  }
1837  }
1838 
1839  apert->type = GERBV_APTYPE_CIRCLE;
1840  apert->nuf_parameters = 1;
1841  apert->parameter[0] = dia;
1842 
1843  /* Add the tool whose definition we just found into the list
1844  * of tools for this layer used to generate statistics. */
1845  if (tool_num != 0) { /* Only add non-zero tool nums.
1846  * Zero = unload command. */
1847  stats = image->drill_stats;
1848  string = g_strdup_printf("%s",
1849  (state->unit == GERBV_UNIT_MM ? _("mm") : _("inch")));
1850  drill_stats_add_to_drill_list(stats->drill_list,
1851  tool_num,
1852  state->unit == GERBV_UNIT_MM ? dia*25.4 : dia,
1853  string);
1854  g_free(string);
1855  }
1856  } /* if(image->aperture[tool_num] == NULL) */
1857 
1858  DPRINTF("<---- ...leaving %s()\n", __FUNCTION__);
1859 
1860  return tool_num;
1861 } /* drill_parse_T_code() */
1862 
1863 
1864 /* -------------------------------------------------------------- */
1865 static drill_m_code_t
1866 drill_parse_M_code(gerb_file_t *fd, drill_state_t *state,
1867  gerbv_image_t *image, unsigned int file_line)
1868 {
1869  gerbv_drill_stats_t *stats = image->drill_stats;
1870  drill_m_code_t m_code;
1871  char op[3];
1872 
1873  DPRINTF("---> entering %s() ...\n", __FUNCTION__);
1874 
1875  op[0] = gerb_fgetc(fd);
1876  op[1] = gerb_fgetc(fd);
1877  op[2] = '\0';
1878 
1879  if (op[0] == EOF
1880  || op[1] == EOF) {
1881  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1882  _("Unexpected EOF found while parsing M-code in file \"%s\""),
1883  fd->filename);
1884 
1885  return DRILL_M_UNKNOWN;
1886  }
1887 
1888  DPRINTF(" Compare M-code \"%s\" at line %u\n", op, file_line);
1889 
1890  switch (m_code = atoi(op)) {
1891  case 0:
1892  /* atoi() return 0 in case of error, recheck string */
1893  if (0 != strncmp(op, "00", 2)) {
1894  m_code = DRILL_M_UNKNOWN;
1895  gerb_ungetc(fd);
1896  gerb_ungetc(fd);
1897  break;
1898  }
1899  stats->M00++;
1900  break;
1901  case 1:
1902  stats->M01++;
1903  break;
1904  case 2:
1905  stats->M02++;
1906  break;
1907  case 14: break; /* M14 - Z-axis route position with depth control */
1908  case 15: break; /* M15 - Z-axis route position (tool down) */
1909  case 16: break; /* M16 - Retract with clamping */
1910  case 17: break; /* M17 - Retract without clamping */
1911  case 18:
1912  stats->M18++;
1913  break;
1914  case 25:
1915  stats->M25++;
1916  break;
1917  case 30:
1918  stats->M30++;
1919  break;
1920  case 45:
1921  stats->M45++;
1922  break;
1923  case 47:
1924  stats->M47++;
1925  break;
1926  case 48:
1927  stats->M48++;
1928  break;
1929  case 70:
1930  stats->M70++;
1931  break;
1932  case 71:
1933  stats->M71++;
1934  eat_line(fd);
1935  break;
1936  case 72:
1937  stats->M72++;
1938  eat_line(fd);
1939  break;
1940  case 80:
1941  stats->M80++;
1942  break;
1943  case 90:
1944  stats->M90++;
1945  break;
1946  case 95:
1947  stats->M95++;
1948  break;
1949  case 97:
1950  stats->M97++;
1951  break;
1952  case 98:
1953  stats->M98++;
1954  break;
1955 
1956  case 6:
1957  case 8: case 9:
1958  case 50: case 51: case 52:
1959  case 60: case 61: case 62: case 63:
1960  stats->M_machine_only++;
1961  break;
1962 
1963  default:
1964  case DRILL_M_UNKNOWN:
1965  break;
1966  }
1967 
1968 
1969  DPRINTF("<---- ...leaving %s()\n", __FUNCTION__);
1970 
1971  return m_code;
1972 } /* drill_parse_M_code() */
1973 
1974 /* -------------------------------------------------------------- */
1975 static int
1976 drill_parse_header_is_metric(gerb_file_t *fd, drill_state_t *state,
1977  gerbv_image_t *image, unsigned int file_line)
1978 {
1979  gerbv_drill_stats_t *stats = image->drill_stats;
1980  char c, op[3];
1981 
1982  DPRINTF(" %s(): entering\n", __FUNCTION__);
1983 
1984  /* METRIC is not an actual M code but a command that is only
1985  * acceptable within the header.
1986  *
1987  * The syntax is
1988  * METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
1989  */
1990 
1991  if (DRILL_HEADER != state->curr_section) {
1992  return 0;
1993  }
1994 
1995  switch (file_check_str(fd, "METRIC")) {
1996  case -1:
1997  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1998  _("Unexpected EOF found while parsing \"%s\" string "
1999  "in file \"%s\""), "METRIC", fd->filename);
2000  return 0;
2001 
2002  case 0:
2003  return 0;
2004  }
2005 
2006  for (;;) {
2007  gboolean found_junk = FALSE;
2008 
2009  if (',' != gerb_fgetc(fd)) {
2010  gerb_ungetc(fd);
2011  eat_line(fd);
2012  break;
2013  }
2014 
2015  /* Is it TZ, LZ, or zerofmt? */
2016  switch (c = gerb_fgetc(fd)) {
2017  case 'T':
2018  case 'L':
2019  if ('Z' != gerb_fgetc(fd)) {
2020  found_junk = TRUE;
2021  break;
2022  }
2023 
2024  if (c == 'L') {
2025  DPRINTF(" %s(): Detected a file that probably has "
2026  "trailing zero suppression\n", __FUNCTION__);
2027  image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
2028  } else {
2029  DPRINTF(" %s(): Detected a file that probably has "
2030  "leading zero suppression\n", __FUNCTION__);
2031  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
2032  }
2033 
2034  if (!state->autod && state->number_format == FMT_USER
2035  && state->digits_before > 0 && c == 'L') {
2036  /* FILE_FORMAT=N:M was seen; for trailing suppression,
2037  * decimals must be N (digits before decimal point). */
2038  state->decimals = state->digits_before;
2039  }
2040 
2041  if (state->autod && state->number_format != FMT_USER) {
2042  /* Default metric number format is 6-digit, 1 um
2043  * resolution. The header number format (for T#C#
2044  * definitions) is fixed to that, while the number
2045  * format within the file can differ. If the
2046  * number_format is already FMT_USER, that means that
2047  * we have set the number format in another way, maybe
2048  * with one of the altium FILE_FORMAT= style comments,
2049  * so don't do this default. */
2050  state->header_number_format =
2051  state->number_format = FMT_000_000;
2052  state->decimals = 3;
2053  }
2054 
2055  if (',' == gerb_fgetc(fd)) {
2056  /* Another option follows */
2057  continue;
2058  }
2059 
2060  gerb_ungetc(fd);
2061  break;
2062 
2063  case '0':
2064  if ('0' != gerb_fgetc(fd)
2065  || '0' != gerb_fgetc(fd)) {
2066  found_junk = TRUE;
2067  break;
2068  }
2069 
2070  /* We just parsed three 0s, the remainder options
2071  so far are: .000 | .00 | 0.00 */
2072  op[0] = gerb_fgetc(fd);
2073  op[1] = gerb_fgetc(fd);
2074  op[2] = '\0';
2075  if (EOF == op[0]
2076  || EOF == op[1]) {
2077  found_junk = TRUE;
2078  break;
2079  }
2080 
2081  if (0 == strcmp(op, "0.")) {
2082  /* expecting FMT_0000_00,
2083  two trailing 0s must follow */
2084  if ('0' != gerb_fgetc(fd)
2085  || '0' != gerb_fgetc(fd)) {
2086  found_junk = TRUE;
2087  break;
2088  }
2089 
2090  eat_line(fd);
2091 
2092  if (state->autod) {
2093  state->number_format = FMT_0000_00;
2094  state->decimals = 2;
2095  }
2096  break;
2097  }
2098 
2099  if (0 != strcmp(op, ".0")) {
2100  found_junk = TRUE;
2101  break;
2102  }
2103 
2104  /* Must be either FMT_000_000 or FMT_000_00, depending
2105  * on whether one or two 0s are following */
2106  if ('0' != gerb_fgetc(fd)) {
2107  found_junk = TRUE;
2108  break;
2109  }
2110 
2111  /* Either FMT_000_000 or FMT_000_00.
2112  * No longer rewind to beginning, as either option
2113  * results in an accepted value. */
2114  {
2115  int last_char = gerb_fgetc(fd);
2116  if (last_char == '0') {
2117  if (state->autod) {
2118  state->number_format = FMT_000_000;
2119  state->decimals = 3;
2120  }
2121  } else {
2122  if (last_char != EOF) {
2123  gerb_ungetc(fd);
2124  }
2125  if (state->autod) {
2126  state->number_format = FMT_000_00;
2127  state->decimals = 2;
2128  }
2129  }
2130  }
2131 
2132  eat_line(fd);
2133  break;
2134 
2135  default:
2136  found_junk = TRUE;
2137  break;
2138  }
2139 
2140  if (found_junk) {
2141  gerb_ungetc(fd);
2142  eat_line(fd);
2143 
2144  gerbv_stats_printf(stats->error_list,
2146  _("Found junk after METRIC command "
2147  "at line %u in file \"%s\""),
2148  file_line, fd->filename);
2149  }
2150 
2151  break;
2152  }
2153 
2154  if (state->autod) {
2155  state->unit = GERBV_UNIT_MM;
2156  }
2157 
2158  return 1;
2159 } /* drill_parse_header_is_metric() */
2160 
2161 /* -------------------------------------------------------------- */
2162 /* Look for a comment like FILE_FORMAT=4:4 and interpret it as if the
2163  * format will be 4 digits before the decimal point and 4 after.
2164  * Return non-zero if we find a FILE_FORMAT header, otherwise return
2165  * 0. */
2166 static int
2167 drill_parse_header_is_metric_comment(gerb_file_t *fd, drill_state_t *state,
2168  gerbv_image_t *image, unsigned int file_line) {
2169  gerbv_drill_stats_t *stats = image->drill_stats;
2170 
2171  DPRINTF(" %s(): entering\n", __FUNCTION__);
2172  /* The leading semicolon is already gone. */
2173  if (DRILL_HEADER != state->curr_section) {
2174  return 0;
2175  }
2176 
2177  switch (file_check_str(fd, "FILE_FORMAT")) {
2178  case -1:
2179  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2180  _("Unexpected EOF found while parsing \"%s\" string "
2181  "in file \"%s\" on line %u"),
2182  "FILE_FORMAT", fd->filename, file_line);
2183  return 0;
2184  case 0:
2185  return 0;
2186  }
2187 
2188  eat_whitespace(fd);
2189  if (file_check_str(fd, "=") != 1) {
2190  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2191  _("Expected '=' while parsing \"%s\" string "
2192  "in file \"%s\" on line %u"),
2193  "FILE_FORMAT", fd->filename, file_line);
2194  return 0;
2195  }
2196  eat_whitespace(fd);
2197  int len = -1;
2198  int digits_before = gerb_fgetint(fd, &len);
2199  if (len < 1) {
2200  /* We've failed to read a number. */
2201  gerbv_stats_printf(
2202  stats->error_list, GERBV_MESSAGE_ERROR, -1,
2203  _("Expected integer after '=' while parsing \"%s\" string "
2204  "in file \"%s\" on line %u"),
2205  "FILE_FORMAT", fd->filename, file_line);
2206  return 0;
2207  }
2208  eat_whitespace(fd);
2209  if (file_check_str(fd, ":") != 1) {
2210  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2211  _("Expected ':' while parsing \"%s\" string "
2212  "in file \"%s\" on line %u"),
2213  "FILE_FORMAT", fd->filename, file_line);
2214  return 0;
2215  }
2216  eat_whitespace(fd);
2217  len = -1;
2218  int digits_after = gerb_fgetint(fd, &len);
2219  if (len < 1) {
2220  gerbv_stats_printf(
2221  stats->error_list, GERBV_MESSAGE_ERROR, -1,
2222  _("Expected integer after ':' while parsing \"%s\" string "
2223  "in file \"%s\" on line %u"),
2224  "FILE_FORMAT", fd->filename, file_line);
2225  /* We've failed to read a number. */
2226  return 0;
2227  }
2228  state->header_number_format = state->number_format = FMT_USER;
2229  state->decimals = digits_after;
2230  state->digits_before = digits_before;
2231  state->autod = 0;
2232  return 1;
2233 } /* drill_parse_header_is_metric_comment() */
2234 
2235 /* -------------------------------------------------------------- */
2236 static int
2237 drill_parse_header_is_inch(gerb_file_t *fd, drill_state_t *state,
2238  gerbv_image_t *image, unsigned int file_line)
2239 {
2240  gerbv_drill_stats_t *stats = image->drill_stats;
2241  char c;
2242 
2243  DPRINTF(" %s(): entering\n", __FUNCTION__);
2244 
2245  if (DRILL_HEADER != state->curr_section) {
2246  return 0;
2247  }
2248 
2249  switch (file_check_str(fd, "INCH")) {
2250  case -1:
2251  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2252  _("Unexpected EOF found while parsing \"%s\" string "
2253  "in file \"%s\""), "INCH", fd->filename);
2254  return 0;
2255 
2256  case 0:
2257  return 0;
2258  }
2259 
2260  /* Look for TZ/LZ */
2261  if (',' != gerb_fgetc(fd)) {
2262  /* Unget the char in case we just advanced past a new line char */
2263  gerb_ungetc (fd);
2264  } else {
2265  c = gerb_fgetc(fd);
2266  if (c != EOF && 'Z' == gerb_fgetc(fd)) {
2267  switch (c) {
2268  case 'L':
2269  image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
2270  if (state->autod) {
2271  state->header_number_format =
2272  state->number_format = FMT_00_0000;
2273  state->decimals = 4;
2274  } else if (state->number_format == FMT_USER
2275  && state->digits_before > 0) {
2276  /* FILE_FORMAT=N:M was seen; for trailing suppression,
2277  * decimals must be N (digits before decimal point). */
2278  state->decimals = state->digits_before;
2279  }
2280  break;
2281 
2282  case 'T':
2283  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
2284  if (state->autod) {
2285  state->header_number_format =
2286  state->number_format = FMT_00_0000;
2287  state->decimals = 4;
2288  }
2289  /* For TZ (leading suppression), decimals stays as M (digits
2290  * after decimal, already set by FILE_FORMAT parser). */
2291  break;
2292 
2293  default:
2294  gerbv_stats_printf(stats->error_list,
2296  _("Found junk '%s' after "
2297  "INCH command "
2298  "at line %u in file \"%s\""),
2299  gerbv_escape_char(c),
2300  file_line, fd->filename);
2301  break;
2302  }
2303  } else {
2304  gerbv_stats_printf(stats->error_list,
2306  _("Found junk '%s' after INCH command "
2307  "at line %u in file \"%s\""),
2308  gerbv_escape_char(c),
2309  file_line, fd->filename);
2310  }
2311  }
2312 
2313  if (state->autod) {
2314  state->unit = GERBV_UNIT_INCH;
2315  }
2316 
2317  return 1;
2318 } /* drill_parse_header_is_inch() */
2319 
2320 /* -------------------------------------------------------------- */
2321 /* Check "ICI" incremental input of coordinates */
2322 static int
2323 drill_parse_header_is_ici(gerb_file_t *fd, drill_state_t *state,
2324  gerbv_image_t *image, unsigned int file_line)
2325 {
2326  gerbv_drill_stats_t *stats = image->drill_stats;
2327 
2328  switch (file_check_str(fd, "ICI,ON")) {
2329  case -1:
2330  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2331  _("Unexpected EOF found while parsing \"%s\" string "
2332  "in file \"%s\""), "ICI,ON", fd->filename);
2333  return 0;
2334 
2335  case 1:
2336  state->coordinate_mode = DRILL_MODE_INCREMENTAL;
2337  return 1;
2338  }
2339 
2340  switch (file_check_str(fd, "ICI,OFF")) {
2341  case -1:
2342  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2343  _("Unexpected EOF found while parsing \"%s\" string "
2344  "in file \"%s\""), "ICI,OFF", fd->filename);
2345  return 0;
2346 
2347  case 1:
2348  state->coordinate_mode = DRILL_MODE_ABSOLUTE;
2349  return 1;
2350  }
2351 
2352  return 0;
2353 } /* drill_parse_header_is_ici() */
2354 
2355 /* -------------------------------------------------------------- */
2356 static drill_g_code_t
2357 drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image, unsigned int file_line)
2358 {
2359  char op[3];
2360  drill_g_code_t g_code;
2361  gerbv_drill_stats_t *stats = image->drill_stats;
2362 
2363  DPRINTF("---> entering %s()...\n", __FUNCTION__);
2364 
2365  op[0] = gerb_fgetc(fd);
2366  op[1] = gerb_fgetc(fd);
2367  op[2] = '\0';
2368 
2369  if (op[0] == EOF
2370  || op[1] == EOF) {
2371  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2372  _("Unexpected EOF found while parsing G-code in file \"%s\""),
2373  fd->filename);
2374  return DRILL_G_UNKNOWN;
2375  }
2376 
2377  DPRINTF(" Compare G-code \"%s\" at line %u\n", op, file_line);
2378 
2379  switch (g_code = atoi(op)) {
2380  case 0:
2381  /* atoi() return 0 in case of error, recheck string */
2382  if (0 != strncmp(op, "00", 2)) {
2383  g_code = DRILL_G_UNKNOWN;
2384  gerb_ungetc(fd);
2385  gerb_ungetc(fd);
2386  break;
2387  }
2388  stats->G00++;
2389  break;
2390  case 1:
2391  stats->G01++;
2392  break;
2393  case 2:
2394  stats->G02++;
2395  break;
2396  case 3:
2397  stats->G03++;
2398  break;
2399  case 5:
2400  stats->G05++;
2401  break;
2402  case 85:
2403  stats->G85++;
2404  break;
2405  case 87:
2406  stats->G87++;
2407  break;
2408  case 90:
2409  stats->G90++;
2410  break;
2411  case 91:
2412  stats->G91++;
2413  break;
2414  case 93:
2415  stats->G93++;
2416  break;
2417 
2418  case 7:
2419  case 34: case 35: case 36: case 37: case 38: case 39:
2420  case 40: case 41: case 42:
2421  case 45: case 46: case 47: case 48:
2422  case 81: case 82: case 83: case 84:
2423  stats->G_machine_only++;
2424  break;
2425 
2426  case DRILL_G_UNKNOWN:
2427  default:
2428  stats->G_unknown++;
2429  break;
2430  }
2431 
2432  DPRINTF("<---- ...leaving %s()\n", __FUNCTION__);
2433 
2434  return g_code;
2435 } /* drill_parse_G_code() */
2436 
2437 
2438 /* -------------------------------------------------------------- */
2439 /* Parse on drill file coordinate.
2440  Returns nothing, but modifies state */
2441 static void
2442 drill_parse_coordinate(gerb_file_t *fd, char firstchar,
2443  gerbv_image_t *image, drill_state_t *state,
2444  unsigned int file_line)
2445 {
2446  gerbv_drill_stats_t *stats = image->drill_stats;
2447 
2448  double x = 0;
2449  gboolean found_x = FALSE;
2450  double y = 0;
2451  gboolean found_y = FALSE;
2452  double i_val = 0;
2453  gboolean found_i = FALSE;
2454  double j_val = 0;
2455  gboolean found_j = FALSE;
2456 
2457 
2458  while (TRUE) {
2459  if (firstchar == 'X') {
2460  x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
2461  found_x = TRUE;
2462  } else if (firstchar == 'Y') {
2463  y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
2464  found_y = TRUE;
2465  } else if (firstchar == 'I') {
2466  i_val = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
2467  found_i = TRUE;
2468  } else if (firstchar == 'J') {
2469  j_val = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
2470  found_j = TRUE;
2471  } else if (firstchar == 'A') {
2472  state->arc_radius = read_double(fd, state->number_format,
2473  image->format->omit_zeros, state->decimals);
2474  state->found_arc_radius = TRUE;
2475  } else {
2476  gerb_ungetc(fd);
2477  break;
2478  }
2479  eat_whitespace(fd);
2480  firstchar = gerb_fgetc(fd);
2481  }
2482  /* Apply axis transforms (M70/M80/M90) to raw parsed values */
2483  if (state->swap_axis) {
2484  double tmp = x; x = y; y = tmp;
2485  gboolean ftmp = found_x; found_x = found_y; found_y = ftmp;
2486  }
2487  if (state->mirror_x && found_x)
2488  x = -x;
2489  if (state->mirror_y && found_y)
2490  y = -y;
2491 
2492  if(state->coordinate_mode == DRILL_MODE_ABSOLUTE) {
2493  if (found_x) {
2494  state->curr_x = x;
2495  }
2496  if (found_y) {
2497  state->curr_y = y;
2498  }
2499  } else if(state->coordinate_mode == DRILL_MODE_INCREMENTAL) {
2500  if (found_x) {
2501  state->curr_x += x;
2502  }
2503  if (found_y) {
2504  state->curr_y += y;
2505  }
2506  } else {
2507  gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
2508  _("Coordinate mode is not absolute and not incremental "
2509  "at line %u in file \"%s\""),
2510  file_line, fd->filename);
2511  }
2512 
2513  /* Store arc center offsets for G02/G03 */
2514  if (found_i)
2515  state->delta_cp_x = i_val;
2516  if (found_j)
2517  state->delta_cp_y = j_val;
2518 } /* drill_parse_coordinate */
2519 
2520 
2521 /* Allocates and returns a new drill_state structure
2522  Returns state pointer on success, NULL on ERROR */
2523 static drill_state_t *
2524 new_state(drill_state_t *state)
2525 {
2526  state = g_new0(drill_state_t, 1);
2527  if (state != NULL) {
2528  /* Init structure */
2529  state->curr_section = DRILL_NONE;
2530  state->coordinate_mode = DRILL_MODE_ABSOLUTE;
2531  state->origin_x = 0.0;
2532  state->origin_y = 0.0;
2533  state->unit = GERBV_UNIT_UNSPECIFIED;
2534  state->backup_number_format = FMT_000_000; /* only used for METRIC */
2535  state->header_number_format = state->number_format = FMT_00_0000; /* i. e. INCH */
2536  state->autod = 1;
2537  state->decimals = 4;
2538  state->route_mode = DRILL_G_DRILL;
2539  state->tool_down = FALSE;
2540  }
2541 
2542  return state;
2543 } /* new_state */
2544 
2545 
2546 /* -------------------------------------------------------------- */
2547 /* Reads one double from fd and returns it.
2548  If a decimal point is found, fmt is not used. */
2549 static double
2550 read_double(gerb_file_t *fd, number_fmt_t fmt, gerbv_omit_zeros_t omit_zeros, int decimals)
2551 {
2552  int read;
2553  char temp[DRILL_READ_DOUBLE_SIZE];
2554  unsigned int i = 0;
2555  double result;
2556  gboolean decimal_point = FALSE;
2557  gboolean sign_prepend = FALSE;
2558 
2559  memset(temp, 0, sizeof(temp));
2560 
2561  read = gerb_fgetc(fd);
2562  while(read != EOF && i < (DRILL_READ_DOUBLE_SIZE -1) &&
2563  (isdigit(read) || read == '.' || read == ',' || read == '+' || read == '-')) {
2564  if(read == ',' || read == '.') decimal_point = TRUE;
2565 
2566  /*
2567  * FIXME -- if we are going to do this, don't we need a
2568  * locale-independent strtod()? I think pcb has one.
2569  */
2570  if(read == ',') {
2571  read = '.'; /* adjust for strtod() */
2572  }
2573 
2574  if(read == '-' || read == '+') {
2575  sign_prepend = TRUE;
2576  }
2577 
2578  temp[i++] = (char)read;
2579  read = gerb_fgetc(fd);
2580  }
2581 
2582  temp[i] = 0;
2583  gerb_ungetc(fd);
2584 
2585  if (decimal_point) {
2586  result = strtod(temp, NULL);
2587  } else {
2588  unsigned int wantdigits;
2589  double scale;
2590  char tmp2[DRILL_READ_DOUBLE_SIZE];
2591 
2592  memset(tmp2, 0, sizeof(tmp2));
2593 
2594  /* Nothing to take care for when leading zeros are
2595  omitted. */
2596  if (omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
2597  switch (fmt) {
2598  case FMT_00_0000:
2599  wantdigits = 2;
2600  break;
2601 
2602  case FMT_000_000:
2603  wantdigits = 3;
2604  break;
2605 
2606  case FMT_0000_00:
2607  wantdigits = 4;
2608  break;
2609 
2610  case FMT_000_00:
2611  wantdigits = 3;
2612  break;
2613 
2614  case FMT_USER:
2615  wantdigits = decimals;
2616  break;
2617 
2618  default:
2619  /* cannot happen, just plugs a compiler warning */
2620  fprintf(stderr, _("%s(): omit_zeros == GERBV_OMIT_ZEROS_TRAILING but fmt = %d.\n"
2621  "This should never have happened\n"), __FUNCTION__, fmt);
2622  return 0;
2623  }
2624 
2625  /* need to add an extra char for '+' or '-' */
2626  if (sign_prepend) {
2627  wantdigits++;
2628  }
2629 
2630 
2631  /*
2632  * we need at least wantdigits + one for the decimal place
2633  * + one for the terminating null character
2634  */
2635  if (wantdigits > sizeof(tmp2) - 2) {
2636  fprintf(stderr, _("%s(): wantdigits = %d which exceeds the maximum allowed size\n"),
2637  __FUNCTION__, wantdigits);
2638  return 0;
2639  }
2640 
2641  /*
2642  * After we have read the correct number of digits
2643  * preceeding the decimal point, insert a decimal point
2644  * and append the rest of the digits.
2645  */
2646  DPRINTF("%s(): wantdigits = %d, strlen(\"%s\") = %ld\n",
2647  __FUNCTION__, wantdigits, temp, (long) strlen(temp));
2648  for (i = 0 ; i < wantdigits && i < strlen(temp) ; i++) {
2649  tmp2[i] = temp[i];
2650  }
2651  for ( ; i < wantdigits ; i++) {
2652  tmp2[i] = '0';
2653  }
2654  tmp2[i++] = '.';
2655  for ( ; i <= strlen(temp) ; i++) {
2656  tmp2[i] = temp[i-1];
2657  }
2658  DPRINTF("%s(): After dealing with trailing zero suppression, convert \"%s\"\n", __FUNCTION__, tmp2);
2659  scale = 1.0;
2660 
2661  for (i = 0 ; i <= strlen(tmp2) && i < sizeof (temp) ; i++) {
2662  temp[i] = tmp2[i];
2663  }
2664 
2665  } else {
2666 
2667  /*
2668  * figure out the scale factor when we are not suppressing
2669  * trailing zeros.
2670  */
2671  switch (fmt) {
2672  case FMT_00_0000:
2673  scale = 1E-4;
2674  break;
2675 
2676  case FMT_000_000:
2677  scale = 1E-3;
2678  break;
2679 
2680  case FMT_000_00:
2681  case FMT_0000_00:
2682  scale = 1E-2;
2683  break;
2684 
2685  case FMT_USER:
2686  scale = pow (10.0, -1.0*decimals);
2687  break;
2688 
2689  default:
2690  /* cannot happen, just plugs a compiler warning */
2691  fprintf (stderr, _("%s(): Unhandled fmt ` %d\n"), __FUNCTION__, fmt);
2692  exit (1);
2693  }
2694  }
2695 
2696  result = strtod(temp, NULL) * scale;
2697  }
2698 
2699  DPRINTF(" %s()=%f: fmt=%d, omit_zeros=%d, decimals=%d \n",
2700  __FUNCTION__, result, fmt, omit_zeros, decimals);
2701 
2702  return result;
2703 } /* read_double */
2704 
2705 /* -------------------------------------------------------------- */
2706 /* Eats all characters up to and including
2707  the first one of CR or LF */
2708 static void
2709 eat_line(gerb_file_t *fd)
2710 {
2711  int read;
2712 
2713  do {
2714  read = gerb_fgetc(fd);
2715  } while (read != '\n' && read != '\r' && read != EOF);
2716 
2717  /* Restore new line character for processing */
2718  if (read != EOF) {
2719  gerb_ungetc(fd);
2720  }
2721 } /* eat_line */
2722 
2723 /* -------------------------------------------------------------- */
2724 /* Eats all tabs and spaces. */
2725 static void
2726 eat_whitespace(gerb_file_t *fd)
2727 {
2728  int read;
2729 
2730  do {
2731  read = gerb_fgetc(fd);
2732  } while ((read == ' ' || read == '\t') && read != EOF);
2733 
2734  /* Restore the non-whitespace character for processing */
2735  if (read != EOF) {
2736  gerb_ungetc(fd);
2737  }
2738 } /* eat_whitespace */
2739 
2740 /* -------------------------------------------------------------- */
2741 static char *
2742 get_line(gerb_file_t *fd)
2743 {
2744  int read;
2745  gchar *retstring;
2746  gchar *tmps=g_strdup("");
2747 
2748  read = gerb_fgetc(fd);
2749  while (read != '\n' && read != '\r' && read != EOF) {
2750  retstring = g_strdup_printf("%s%c", tmps, read);
2751 
2752  /* since g_strdup_printf allocates memory, we need to free it */
2753  if (tmps) {
2754  g_free (tmps);
2755  tmps = NULL;
2756  }
2757  tmps = retstring;
2758  read = gerb_fgetc(fd);
2759  }
2760 
2761  /* Restore new line character for processing */
2762  if (read != EOF) {
2763  gerb_ungetc(fd);
2764  }
2765 
2766  return tmps;
2767 } /* get_line */
2768 
2769 /* -------------------------------------------------------------- */
2770 /* Look for str in the file fd. If found, return 1. If not, return
2771  * 0. If EOF is reached while searching, return -1. If the find
2772  * fails, rewinds the file descriptor. Otherwise, it doesn't and the
2773  * string is consumed.
2774  */
2775 static int
2776 file_check_str(gerb_file_t *fd, const char *str)
2777 {
2778  char c;
2779 
2780  for (int i = 0; str[i] != '\0'; i++) {
2781 
2782  c = gerb_fgetc(fd);
2783 
2784  if (c == EOF) {
2785  return -1;
2786  }
2787 
2788  if (c != str[i]) {
2789  do {
2790  /* Restore checked string */
2791  gerb_ungetc(fd);
2792  } while (i--);
2793 
2794  return 0;
2795  }
2796  }
2797 
2798  return 1;
2799 }
2800 
2801 /* -------------------------------------------------------------- */
2803 const char *drill_g_code_name(drill_g_code_t g_code)
2804 {
2805  switch (g_code) {
2806  case DRILL_G_ROUT:
2807  return N_("rout mode");
2808  case DRILL_G_LINEARMOVE:
2809  return N_("linear mode");
2810  case DRILL_G_CWMOVE:
2811  return N_("circular CW mode");
2812  case DRILL_G_CCWMOVE:
2813  return N_("circular CCW mode");
2814  case DRILL_G_VARIABLEDWELL:
2815  return N_("variable dwell");
2816  case DRILL_G_DRILL:
2817  return N_("drill mode");
2818  case DRILL_G_OVERRIDETOOLSPEED:
2819  return N_("override tool feed or speed");
2820  case DRILL_G_ROUTCIRCLE:
2821  return N_("routed CW circle");
2822  case DRILL_G_ROUTCIRCLECCW:
2823  return N_("routed CCW circle");
2824  case DRILL_G_VISTOOL:
2825  return N_("select vision tool");
2826  case DRILL_G_VISSINGLEPOINTOFFSET:
2827  return N_("single point vision offset");
2828  case DRILL_G_VISMULTIPOINTTRANS:
2829  return N_("multipoint vision translation");
2830  case DRILL_G_VISCANCEL:
2831  return N_("cancel vision translation or offset");
2832  case DRILL_G_VISCORRHOLEDRILL:
2833  return N_("vision corrected single hole drilling");
2834  case DRILL_G_VISAUTOCALIBRATION:
2835  return N_("vision system autocalibration");
2836  case DRILL_G_CUTTERCOMPOFF:
2837  return N_("cutter compensation off");
2838  case DRILL_G_CUTTERCOMPLEFT:
2839  return N_("cutter compensation left");
2840  case DRILL_G_CUTTERCOMPRIGHT:
2841  return N_("cutter compensation right");
2842  case DRILL_G_VISSINGLEPOINTOFFSETREL:
2843  return N_("single point vision relative offset");
2844  case DRILL_G_VISMULTIPOINTTRANSREL:
2845  return N_("multipoint vision relative translation");
2846  case DRILL_G_VISCANCELREL:
2847  return N_("cancel vision relative translation or offset");
2848  case DRILL_G_VISCORRHOLEDRILLREL:
2849  return N_("vision corrected single hole relative drilling");
2850  case DRILL_G_PACKDIP2:
2851  return N_("dual in line package");
2852  case DRILL_G_PACKDIP:
2853  return N_("dual in line package");
2854  case DRILL_G_PACK8PINL:
2855  return N_("eight pin L package");
2856  case DRILL_G_CIRLE:
2857  return N_("canned circle");
2858  case DRILL_G_SLOT:
2859  return N_("canned slot");
2860  case DRILL_G_ROUTSLOT:
2861  return N_("routed step slot");
2862  case DRILL_G_ABSOLUTE:
2863  return N_("absolute input mode");
2864  case DRILL_G_INCREMENTAL:
2865  return N_("incremental input mode");
2866  case DRILL_G_ZEROSET:
2867  return N_("zero set");
2868 
2869  case DRILL_G_UNKNOWN:
2870  default:
2871  return N_("unknown G-code");
2872  }
2873 } /* drill_g_code_name() */
2874 
2875 /* -------------------------------------------------------------- */
2877 const char *drill_m_code_name(drill_m_code_t m_code)
2878 {
2879  switch (m_code) {
2880  case DRILL_M_END:
2881  return N_("end of program");
2882  case DRILL_M_PATTERNEND:
2883  return N_("pattern end");
2884  case DRILL_M_REPEATPATTERNOFFSET:
2885  return N_("repeat pattern offset");
2886  case DRILL_M_STOPOPTIONAL:
2887  return N_("stop optional");
2888  case DRILL_M_SANDREND:
2889  return N_("step and repeat end");
2890  case DRILL_M_STOPINSPECTION:
2891  return N_("stop for inspection");
2892  case DRILL_M_ZAXISROUTEPOSITIONDEPTHCTRL:
2893  return N_("Z-axis rout position with depth control");
2894  case DRILL_M_ZAXISROUTEPOSITION:
2895  return N_("Z-axis rout position");
2896  case DRILL_M_RETRACTCLAMPING:
2897  return N_("retract with clamping");
2898  case DRILL_M_RETRACTNOCLAMPING:
2899  return N_("retract without clamping");
2900  case DRILL_M_TOOLTIPCHECK:
2901  return N_("tool tip check");
2902  case DRILL_M_PATTERN:
2903  return N_("pattern start");
2904  case DRILL_M_ENDREWIND:
2905  return N_("end of program with rewind");
2906  case DRILL_M_MESSAGELONG:
2907  return N_("long operator message");
2908  case DRILL_M_MESSAGE:
2909  return N_("operator message");
2910  case DRILL_M_HEADER:
2911  return N_("header start");
2912  case DRILL_M_VISANDRPATTERN:
2913  return N_("vision step and repeat pattern start");
2914  case DRILL_M_VISANDRPATTERNREWIND:
2915  return N_("vision step and repeat rewind");
2916  case DRILL_M_VISANDRPATTERNOFFSETCOUNTERCTRL:
2917  return N_("vision step and repeat offset counter control");
2918  case DRILL_M_REFSCALING:
2919  return N_("reference scaling on");
2920  case DRILL_M_REFSCALINGEND:
2921  return N_("reference scaling off");
2922  case DRILL_M_PECKDRILLING:
2923  return N_("peck drilling on");
2924  case DRILL_M_PECKDRILLINGEND:
2925  return N_("peck drilling off");
2926  case DRILL_M_SWAPAXIS:
2927  return N_("swap axes");
2928  case DRILL_M_METRIC:
2929  return N_("metric measuring mode");
2930  case DRILL_M_IMPERIAL:
2931  return N_("inch measuring mode");
2932  case DRILL_M_MIRRORX:
2933  return N_("mirror image X-axis");
2934  case DRILL_M_MIRRORY:
2935  return N_("mirror image Y-axis");
2936  case DRILL_M_HEADEREND:
2937  return N_("header end");
2938  case DRILL_M_CANNEDTEXTX:
2939  return N_("canned text along X-axis");
2940  case DRILL_M_CANNEDTEXTY:
2941  return N_("canned text along Y-axis");
2942  case DRILL_M_USERDEFPATTERN:
2943  return N_("user defined stored pattern");
2944 
2945  case DRILL_M_UNKNOWN:
2946  default:
2947  return N_("unknown M-code");
2948  }
2949 } /* drill_m_code_name() */
Dynamic GUI window creation header info.
const char * drill_g_code_name(drill_g_code_t g_code)
Return drill G-code name by code number.
Definition: drill.c:2803
const char * drill_m_code_name(drill_m_code_t m_code)
Return drill M-code name by code number.
Definition: drill.c:2877
Header info for the Excellon drill parsing functions.
gerbv_drill_stats_t * gerbv_drill_stats_new(void)
Allocates a new drill_stats structure.
Definition: drill_stats.c:48
Header info to the statistics generating functions for Excellon drill files.
void gerbv_destroy_image(gerbv_image_t *image)
Free an image structure.
Definition: gerb_image.c:106
gerbv_image_t * gerbv_create_image(gerbv_image_t *image, const gchar *type)
Allocate a new gerbv_image structure.
Definition: gerb_image.c:46
Header info for the RS274X parsing functions.
The main header file for the libgerbv library.
@ GERBV_APERTURE_STATE_ON
Definition: gerbv.h:179
@ GERBV_APERTURE_STATE_FLASH
Definition: gerbv.h:180
gerbv_omit_zeros_t
Definition: gerbv.h:288
@ GERBV_OMIT_ZEROS_TRAILING
Definition: gerbv.h:289
@ GERBV_OMIT_ZEROS_EXPLICIT
Definition: gerbv.h:290
@ GERBV_OMIT_ZEROS_LEADING
Definition: gerbv.h:288
@ GERBV_OMIT_ZEROS_UNSPECIFIED
Definition: gerbv.h:291
@ GERBV_MESSAGE_ERROR
Definition: gerbv.h:150
@ GERBV_MESSAGE_NOTE
Definition: gerbv.h:152
@ GERBV_MESSAGE_WARNING
Definition: gerbv.h:151
@ GERBV_APTYPE_CIRCLE
Definition: gerbv.h:160
gerbv_unit_t
Definition: gerbv.h:275
@ GERBV_UNIT_INCH
Definition: gerbv.h:275
@ GERBV_UNIT_MM
Definition: gerbv.h:276
@ GERBV_UNIT_UNSPECIFIED
Definition: gerbv.h:277
@ GERBV_INTERPOLATION_CW_CIRCULAR
Definition: gerbv.h:306
@ GERBV_INTERPOLATION_CCW_CIRCULAR
Definition: gerbv.h:307
@ GERBV_INTERPOLATION_LINEARx1
Definition: gerbv.h:302
@ GERBV_LAYERTYPE_DRILL
Definition: gerbv.h:329
gerbv_format_t * format
Definition: gerbv.h:734
gerbv_layer_t * layers
Definition: gerbv.h:731
gerbv_layertype_t layertype
Definition: gerbv.h:729
gerbv_net_t * netlist
Definition: gerbv.h:736
gerbv_drill_stats_t * drill_stats
Definition: gerbv.h:738
gerbv_aperture_t * aperture[APERTURE_MAX]
Definition: gerbv.h:730
gerbv_netstate_t * states
Definition: gerbv.h:732
gerbv_image_info_t * info
Definition: gerbv.h:735
gerbv_render_size_t boundingBox
Definition: gerbv.h:668
gerbv_layer_t * layer
Definition: gerbv.h:675
double stop_y
Definition: gerbv.h:667
gerbv_aperture_state_t aperture_state
Definition: gerbv.h:670
double stop_x
Definition: gerbv.h:666
double start_x
Definition: gerbv.h:664
gerbv_netstate_t * state
Definition: gerbv.h:676
struct gerbv_net * next
Definition: gerbv.h:673
double start_y
Definition: gerbv.h:665
gerbv_interpolation_t interpolation
Definition: gerbv.h:671
gerbv_cirseg_t * cirseg
Definition: gerbv.h:672
int aperture
Definition: gerbv.h:669
gerbv_unit_t unit
Definition: gerbv.h:654