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