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