gerbv  2.6A
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 #include <locale.h>
39 
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 
44 #include <math.h> /* pow() */
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 #define dprintf if(DEBUG) printf
61 
62 #define MAXL 200
63 #define DRILL_READ_DOUBLE_SIZE 32
64 
65 typedef enum {
66  DRILL_NONE, DRILL_HEADER, DRILL_DATA
67 } drill_file_section_t;
68 
69 typedef enum {
70  DRILL_MODE_ABSOLUTE, DRILL_MODE_INCREMENTAL
71 } drill_coordinate_mode_t;
72 
73 typedef enum {
74  DRILL_M_UNKNOWN, DRILL_M_NOT_IMPLEMENTED,
75  DRILL_M_END, DRILL_M_ENDREWIND,
76  DRILL_M_MESSAGE, DRILL_M_LONGMESSAGE,
77  DRILL_M_HEADER, DRILL_M_ENDHEADER,
78  DRILL_M_METRIC, DRILL_M_IMPERIAL,
79  DRILL_M_BEGINPATTERN, DRILL_M_ENDPATTERN,
80  DRILL_M_CANNEDTEXT, DRILL_M_TIPCHECK,
81  DRILL_M_METRICHEADER, DRILL_M_IMPERIALHEADER
82 } drill_m_code_t;
83 
84 typedef enum {
85  DRILL_G_ABSOLUTE, DRILL_G_INCREMENTAL,
86  DRILL_G_ZEROSET, DRILL_G_UNKNOWN,
87  DRILL_G_ROUT, DRILL_G_DRILL,
88  DRILL_G_LINEARMOVE,
89  DRILL_G_CWMOVE, DRILL_G_CCWMOVE,
90  DRILL_G_SLOT
91 } drill_g_code_t;
92 
93 typedef enum {
94  FMT_00_0000 /* INCH */,
95  FMT_000_000 /* METRIC 6-digit, 1 um */,
96  FMT_000_00 /* METRIC 5-digit, 10 um */,
97  FMT_0000_00 /* METRIC 6-digit, 10 um */,
98  FMT_USER /* User defined format */
99 } number_fmt_t;
100 
101 typedef struct drill_state {
102  double curr_x;
103  double curr_y;
104  int current_tool;
105  drill_file_section_t curr_section;
106  drill_coordinate_mode_t coordinate_mode;
107  double origin_x;
108  double origin_y;
109  gerbv_unit_t unit;
110  /* number_format is used throughout the file itself.
111 
112  header_number_format is used to parse the tool definition C
113  codes within the header. It is fixed to FMT_00_0000 for INCH
114  measures, and FMT_000_000 (1 um resolution) for metric
115  measures. */
116  number_fmt_t number_format, header_number_format;
117  /* Used as a backup when temporarily switching to INCH. */
118  number_fmt_t backup_number_format;
119 
120  /* 0 means we don't try to autodetect any of the other values */
121  int autod;
122 
123  /* in FMT_USER this specifies the number of digits before the
124  * decimal point when doing trailing zero supression. Otherwise
125  * it is the number of digits *after* the decimal
126  * place in the file
127  */
128  int decimals;
129 
130 } drill_state_t;
131 
132 /* Local function prototypes */
133 static drill_g_code_t drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image);
134 static drill_m_code_t drill_parse_M_code(gerb_file_t *fd, drill_state_t *state,
135  gerbv_image_t *image);
136 static int drill_parse_T_code(gerb_file_t *fd, drill_state_t *state,
137  gerbv_image_t *image);
138 static void drill_parse_coordinate(gerb_file_t *fd, char firstchar,
139  gerbv_image_t *image, drill_state_t *state);
140 static drill_state_t *new_state(drill_state_t *state);
141 static double read_double(gerb_file_t *fd, number_fmt_t fmt,
142  gerbv_omit_zeros_t omit_zeros, int decimals);
143 static void eat_line(gerb_file_t *fd);
144 static char *get_line(gerb_file_t *fd);
145 
146 /* -------------------------------------------------------------- */
147 /* This is the list of specific attributes a drill file may have from
148  * the point of view of parsing it.
149  */
150 
151 enum {
152  SUP_NONE = 0,
153  SUP_LEAD,
154  SUP_TRAIL
155 };
156 
157 static const char *supression_list[] = {
158  N_("None"),
159  N_("Leading"),
160  N_("Trailing"),
161  0
162 };
163 
164 enum {
165  UNITS_INCH = 0,
166  UNITS_MM
167 };
168 
169 static const char *units_list[] = {
170  N_("inch"),
171  N_("mm"),
172  0
173 };
174 
175 enum {
176  HA_auto = 0,
177  HA_supression,
178  HA_xy_units,
179  HA_digits,
180 #if 0
181  HA_tool_units,
182 #endif
183 };
184 
185 static gerbv_HID_Attribute drill_attribute_list[] = {
186  /* This should be first */
187  {N_("autodetect"), N_("Try to autodetect the file format"),
188  HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
189 
190  {N_("zero_supression"), N_("Zero supression"),
191  HID_Enum, 0, 0, {0, 0, 0}, supression_list, 0},
192 
193  {N_("units"), N_("Units"),
194  HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
195 
196  {N_("digits"), N_("Number of digits. For trailing zero supression,"
197  " this is the number of digits before the decimal point. "
198  "Otherwise this is the number of digits after the decimal point."),
199  HID_Integer, 0, 20, {5, 0, 0}, 0, 0},
200 
201 #if 0
202  {"tool_units", "Tool size units",
203  HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
204 #endif
205 };
206 
207 
208 void
209 drill_attribute_merge (gerbv_HID_Attribute *dest, int ndest, gerbv_HID_Attribute *src, int nsrc)
210 {
211  int i, j;
212 
213  /* Here is a brain dead merge algorithm which shold make anyone cringe.
214  * Still, it is simple and we won't merge many attributes and not
215  * many times either.
216  */
217 
218  for (i = 0 ; i < nsrc ; i++) {
219  /* see if our destination wants this attribute */
220  j = 0;
221  while (j < ndest && strcmp (src[i].name, dest[j].name) != 0)
222  j++;
223 
224  /* if we wanted it and it is the same type, copy it over */
225  if (j < ndest && src[i].type == dest[j].type) {
226  dest[j].default_val = src[i].default_val;
227  }
228  }
229 }
230 
231 /*
232  * Adds the actual drill hole to the drawing
233  */
234 static gerbv_net_t *
235 drill_add_drill_hole (gerbv_image_t *image, drill_state_t *state, gerbv_drill_stats_t *stats, gerbv_net_t *curr_net)
236 {
237  /* Add one to drill stats for the current tool */
238  drill_stats_increment_drill_counter(image->drill_stats->drill_list,
239  state->current_tool);
240 
241  curr_net->next = (gerbv_net_t *)g_malloc0(sizeof(gerbv_net_t));
242  if (curr_net->next == NULL)
243  GERB_FATAL_ERROR(_("malloc curr_net->next failed"));
244 
245  curr_net = curr_net->next;
246  curr_net->layer = image->layers;
247  curr_net->state = image->states;
248  curr_net->start_x = (double)state->curr_x;
249  curr_net->start_y = (double)state->curr_y;
250  /* KLUDGE. This function isn't allowed to return anything
251  but inches */
252  if(state->unit == GERBV_UNIT_MM) {
253  curr_net->start_x /= 25.4;
254  curr_net->start_y /= 25.4;
255  /* KLUDGE. All images, regardless of input format,
256  are returned in INCH format */
257  curr_net->state->unit = GERBV_UNIT_INCH;
258  }
259 
260  curr_net->stop_x = curr_net->start_x - state->origin_x;
261  curr_net->stop_y = curr_net->start_y - state->origin_y;
262  curr_net->aperture = state->current_tool;
264 
265  /* Find min and max of image.
266  Mustn't forget (again) to add the hole radius */
267 
268  /* Check if aperture is set. Ignore the below instead of
269  causing SEGV... */
270  if(image->aperture[state->current_tool] == NULL)
271  return curr_net;
272 
273  curr_net->boundingBox.left=curr_net->start_x -
274  image->aperture[state->current_tool]->parameter[0] / 2;
275  curr_net->boundingBox.right=curr_net->start_x +
276  image->aperture[state->current_tool]->parameter[0] / 2;
277  curr_net->boundingBox.bottom=curr_net->start_y -
278  image->aperture[state->current_tool]->parameter[0] / 2;
279  curr_net->boundingBox.top=curr_net->start_y +
280  image->aperture[state->current_tool]->parameter[0] / 2;
281 
282  image->info->min_x =
283  min(image->info->min_x,
284  (curr_net->start_x -
285  image->aperture[state->current_tool]->parameter[0] / 2));
286  image->info->min_y =
287  min(image->info->min_y,
288  (curr_net->start_y -
289  image->aperture[state->current_tool]->parameter[0] / 2));
290  image->info->max_x =
291  max(image->info->max_x,
292  (curr_net->start_x +
293  image->aperture[state->current_tool]->parameter[0] / 2));
294  image->info->max_y =
295  max(image->info->max_y,
296  (curr_net->start_y +
297  image->aperture[state->current_tool]->parameter[0] / 2));
298 
299  return curr_net;
300 }
301 
302 /* -------------------------------------------------------------- */
304 parse_drillfile(gerb_file_t *fd, gerbv_HID_Attribute *attr_list, int n_attr, int reload)
305 {
306  drill_state_t *state = NULL;
307  gerbv_image_t *image = NULL;
308  gerbv_net_t *curr_net = NULL;
309  int read;
310  gerbv_drill_stats_t *stats;
311  gchar *tmps;
312  gchar *string;
313 
314  /*
315  * many locales redefine "." as "," and so on, so sscanf and strtod
316  * has problems when reading files using %f format.
317  * Fixes bug #1963618 reported by Lorenzo Marcantonio.
318  */
319  setlocale(LC_NUMERIC, "C" );
320 
321  /* Create new image for this layer */
322  dprintf("In parse_drillfile, about to create image for this layer\n");
323 
324  image = gerbv_create_image(image, "Excellon Drill File");
325  if (image == NULL)
326  GERB_FATAL_ERROR(_("malloc image failed"));
327 
328  if (reload && attr_list != NULL) {
329  /* FIXME there should probably just be a function to copy an
330  attribute list including using strdup as needed */
331 
332  image->info->n_attr = n_attr;
333  image->info->attr_list = gerbv_attribute_dup(attr_list, n_attr);
334 
335  } else {
336  /* Copy in the default attribute list for drill files. We make a
337  * copy here because we will allow per-layer editing of the
338  * attributes.
339  */
340  image->info->n_attr = sizeof (drill_attribute_list) / sizeof (drill_attribute_list[0]);
341  image->info->attr_list = gerbv_attribute_dup (drill_attribute_list, image->info->n_attr);
342 
343  /* now merge any project attributes */
344  drill_attribute_merge (image->info->attr_list, image->info->n_attr,
345  attr_list, n_attr);
346  }
347 
348  curr_net = image->netlist;
349  curr_net->layer = image->layers;
350  curr_net->state = image->states;
352  stats = gerbv_drill_stats_new();
353  if (stats == NULL)
354  GERB_FATAL_ERROR(_("malloc stats failed"));
355  image->drill_stats = stats;
356 
357  /* Create local state variable to track photoplotter state */
358  state = new_state(state);
359  if (state == NULL)
360  GERB_FATAL_ERROR(_("malloc state failed"));
361 
362  image->format = (gerbv_format_t *)g_malloc0(sizeof(gerbv_format_t));
363  if (image->format == NULL)
364  GERB_FATAL_ERROR(_("malloc format failed"));
365 
366  image->format->omit_zeros = GERBV_OMIT_ZEROS_UNSPECIFIED;
367 
368  if (!image->info->attr_list[HA_auto].default_val.int_value) {
369  state->autod = 0;
370  state->number_format = FMT_USER;
371  state->decimals = image->info->attr_list[HA_digits].default_val.int_value;
372  if (image->info->attr_list[HA_xy_units].default_val.int_value == UNITS_MM)
373  state->unit = GERBV_UNIT_MM;
374  switch (image->info->attr_list[HA_supression].default_val.int_value) {
375  case SUP_LEAD:
376  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
377  break;
378 
379  case SUP_TRAIL:
380  image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
381  break;
382 
383  default:
384  image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
385  break;
386  }
387  }
388 
389  dprintf("%s(): Starting parsing of drill file\n", __FUNCTION__);
390  while ((read = gerb_fgetc(fd)) != EOF) {
391 
392  switch ((char) read) {
393  case ';' :
394  /* Comment found. Eat rest of line */
395  eat_line(fd);
396  break;
397  case 'D' :
398  gerb_ungetc (fd);
399  tmps = get_line (fd);
400  if (strcmp (tmps, "DETECT,ON") == 0 ||
401  strcmp (tmps, "DETECT,OFF") == 0) {
402  gchar *tmps2;
403  gchar *tmps3;
404  if (strcmp (tmps, "DETECT,ON") == 0)
405  tmps3 = "ON";
406  else
407  tmps3 = "OFF";
408 
409  /* broken tool detect on/off. Silently ignored. */
410  if (stats->detect) {
411  tmps2 = g_strdup_printf ("%s\n%s", stats->detect, tmps3);
412  g_free (stats->detect);
413  } else {
414  tmps2 = g_strdup_printf ("%s", tmps3);
415  }
416  stats->detect = tmps2;
417  } else {
418  string = g_strdup_printf(_("Undefined header line = '%s'\n"),tmps);
419  drill_stats_add_error(stats->error_list,
420  -1,
421  string,
423  g_free(string);
424  }
425  g_free (tmps);
426  break;
427  case 'F' :
428  gerb_ungetc (fd);
429  tmps = get_line (fd);
430  /* Silently ignore FMAT,2. Not sure what others are allowed */
431  if (strcmp (tmps, "FMAT,2") != 0) {
432  string = g_strdup_printf(_("Undefined header line = '%s'\n"),tmps);
433  drill_stats_add_error(stats->error_list,
434  -1,
435  string,
437  g_free(string);
438  }
439  g_free (tmps);
440  break;
441 
442  case 'G':
443  /* Most G codes aren't used, for now */
444  switch (drill_parse_G_code(fd, image)) {
445  case DRILL_G_ROUT :
446  drill_stats_add_error(stats->error_list,
447  -1,
448  _("Rout mode data is not supported\n"),
450  break;
451  case DRILL_G_DRILL :
452  break;
453  case DRILL_G_SLOT :
454  /* Parse cut slot end coords */
455  if ((read = gerb_fgetc(fd)) != EOF) {
456  drill_parse_coordinate(fd, read, image, state);
457 
458  /* Modify last curr_net as cut slot */
459  curr_net->stop_x = (double)state->curr_x;
460  curr_net->stop_y = (double)state->curr_y;
461  if (state->unit == GERBV_UNIT_MM) {
462  /* Convert to inches -- internal units */
463  curr_net->stop_x /= 25.4;
464  curr_net->stop_y /= 25.4;
465  }
467  } else {
468  drill_stats_add_error(stats->error_list,
469  -1, _("Unexpected EOF found.\n"),
471  }
472  break;
473  case DRILL_G_ABSOLUTE :
474  state->coordinate_mode = DRILL_MODE_ABSOLUTE;
475  break;
476  case DRILL_G_INCREMENTAL :
477  state->coordinate_mode = DRILL_MODE_INCREMENTAL;
478  break;
479  case DRILL_G_ZEROSET :
480  if((read = gerb_fgetc(fd)) == EOF)
481  drill_stats_add_error(stats->error_list,
482  -1,
483  _("Unexpected EOF found.\n"),
485  drill_parse_coordinate(fd, (char)read, image, state);
486  state->origin_x = state->curr_x;
487  state->origin_y = state->curr_y;
488  break;
489  default :
490  eat_line(fd);
491  break;
492  }
493  break;
494  case 'I':
495  if (state->curr_section != DRILL_HEADER)
496  break;
497  {
498  int c = gerb_fgetc(fd);
499  switch (c) {
500  case 'N':
501  if ('C' == gerb_fgetc(fd)) {
502  if ('H' == gerb_fgetc(fd)) {
503  state->unit = GERBV_UNIT_INCH;
504 
505  /* Look for TZ/LZ */
506  if (',' == gerb_fgetc(fd)) {
507  c = gerb_fgetc(fd);
508  if (c != EOF && 'Z' == gerb_fgetc(fd)) {
509  switch (c) {
510  case 'L':
511  if (state->autod) {
512  image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
513  state->header_number_format =
514  state->number_format = FMT_00_0000;
515  state->decimals = 4;
516  }
517  break;
518 
519  case 'T':
520  if (state->autod) {
521  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
522  state->header_number_format =
523  state->number_format = FMT_00_0000;
524  state->decimals = 4;
525  }
526  break;
527 
528  default:
529  drill_stats_add_error(stats->error_list,
530  -1,
531  _("Found junk after INCH command\n"),
533  break;
534  }
535  } else {
536  drill_stats_add_error(stats->error_list,
537  -1,
538  _("Found junk after INCH command\n"),
540  }
541  }
542  else
543  /* unget the char in case we just advanced past a new line char */
544  gerb_ungetc (fd);
545  }
546  }
547  break;
548  case 'C':
549  if ('I' == gerb_fgetc(fd))
550  if (',' == gerb_fgetc(fd))
551  if ('O' == gerb_fgetc(fd)) {
552  if ('N' == (c = gerb_fgetc(fd)))
553  state->coordinate_mode = DRILL_MODE_INCREMENTAL;
554  else if ('F' == c) if ('F' == gerb_fgetc(fd))
555  state->coordinate_mode = DRILL_MODE_ABSOLUTE;
556  }
557  break;
558  }
559  eat_line(fd);
560  }
561  break;
562 
563  case 'M':
564  switch(drill_parse_M_code(fd, state, image)) {
565  case DRILL_M_HEADER :
566  state->curr_section = DRILL_HEADER;
567  break;
568  case DRILL_M_ENDHEADER :
569  state->curr_section = DRILL_DATA;
570 
571  if (image->format->omit_zeros == GERBV_OMIT_ZEROS_UNSPECIFIED) {
572  /* Excellon says they default to specify leading
573  zeros, i.e. omit trailing zeros. The Excellon
574  files floating around that don't specify the
575  leading/trailing zeros in the header seem to
576  contradict to this though.
577 
578  XXX We should probably ask the user. */
579 
580  drill_stats_add_error(stats->error_list,
581  -1,
582  _("End of Excellon header reached but no leading/trailing zero handling specified.\n"),
584  drill_stats_add_error(stats->error_list,
585  -1,
586  _("Assuming leading zeros.\n"),
588  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
589  }
590  break;
591  case DRILL_M_METRIC :
592  if (state->unit == GERBV_UNIT_UNSPECIFIED &&
593  state->curr_section != DRILL_HEADER) {
594  drill_stats_add_error(stats->error_list,
595  -1,
596  _("M71 code found but no METRIC specification in header.\n"),
598  drill_stats_add_error(stats->error_list,
599  -1,
600  _("Assuming all tool sizes are MM.\n"),
602  int tool_num;
603  double size;
604  stats = image->drill_stats;
605  for (tool_num = TOOL_MIN; tool_num < TOOL_MAX; tool_num++) {
606  if (image->aperture && image->aperture[tool_num]) {
607  /* First update stats. Do this before changing drill dias.
608  * Maybe also put error into stats? */
609  size = image->aperture[tool_num]->parameter[0];
610  drill_stats_modify_drill_list(stats->drill_list,
611  tool_num,
612  size,
613  "MM");
614  /* Now go back and update all tool dias, since
615  * tools are displayed in inch units
616  */
617  image->aperture[tool_num]->parameter[0] /= 25.4;
618  }
619  }
620  }
621  if (state->autod) {
622  state->number_format = state->backup_number_format;
623  state->unit = GERBV_UNIT_MM;
624  }
625  break;
626  case DRILL_M_IMPERIAL :
627  if (state->autod) {
628  if (state->number_format != FMT_00_0000)
629  /* save metric format definition for later */
630  state->backup_number_format = state->number_format;
631  state->number_format = FMT_00_0000;
632  state->decimals = 4;
633  state->unit = GERBV_UNIT_INCH;
634  }
635 
636  break;
637  case DRILL_M_LONGMESSAGE :
638  case DRILL_M_MESSAGE :
639  case DRILL_M_CANNEDTEXT :
640  tmps = get_line(fd);
641  string = g_strdup_printf(_("Message embedded in drill file: '%s'\n"),
642  tmps);
643  drill_stats_add_error(stats->error_list,
644  -1,
645  string,
647  g_free(tmps);
648  break;
649  case DRILL_M_NOT_IMPLEMENTED :
650  case DRILL_M_ENDPATTERN :
651  case DRILL_M_TIPCHECK :
652  break;
653  case DRILL_M_END :
654  /* M00 has optional arguments */
655  eat_line(fd);
656  case DRILL_M_ENDREWIND :
657  goto drill_parse_end;
658  break;
659  case DRILL_M_METRICHEADER :
660  state->unit = GERBV_UNIT_MM;
661  break;
662  default:
663  drill_stats_add_error(stats->error_list,
664  -1,
665  _("Undefined M code found.\n"),
667  }
668  break;
669 
670  case 'R':
671  if(state->curr_section == DRILL_HEADER) {
672  drill_stats_add_error(stats->error_list,
673  -1,
674  _("R codes are not allowed in the header.\n"),
676  } else {
677  double start_x, start_y;
678  double step_x, step_y;
679  int c;
680  int rcnt;
681  /*
682  * This is the "Repeat hole" command. Format is:
683  * R##[X##][Y##]
684  * This repeats the previous hole stepping in the X and
685  * Y increments give. Example:
686  * R04X0.1 -- repeats the drill hole 4 times, stepping
687  * 0.1" in the X direction. Note that the X and Y step
688  * sizes default to zero if not given and that they use
689  * the same format and units as the normal X,Y
690  * coordinates.
691  */
692  stats->R++;
693 
694  start_x = state->curr_x;
695  start_y = state->curr_y;
696 
697 
698 
699  /* figure out how many repeats there are */
700  c = gerb_fgetc (fd);
701  rcnt = 0;
702  while ( '0' <= c && c <= '9') {
703  rcnt = 10*rcnt + (c - '0');
704  c = gerb_fgetc (fd);
705  }
706  dprintf ("working on R code (repeat) with a number of reps equal to %d\n", rcnt);
707 
708  step_x = 0.0;
709  if (c == 'X') {
710  step_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
711  c = gerb_fgetc (fd);
712  }
713 
714  step_y = 0.0;
715  if( c == 'Y') {
716  step_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
717  } else {
718  gerb_ungetc (fd);
719  }
720 
721  dprintf ("Getting ready to repeat the drill %d times with delta_x = %g, delta_y = %g\n", rcnt, step_x, step_y);
722 
723  /* spit out the drills */
724  for (c = 1 ; c <= rcnt ; c++) {
725  state->curr_x = start_x + c*step_x;
726  state->curr_y = start_y + c*step_y;
727  dprintf (" Repeat #%d -- new location is (%g, %g)\n", c, state->curr_x, state->curr_y);
728  curr_net = drill_add_drill_hole (image, state, stats, curr_net);
729  }
730 
731  }
732 
733  case 'S':
734  drill_stats_add_error(stats->error_list,
735  -1,
736  _("Drill file sets spindle speed -- ignoring.\n"),
738  eat_line(fd);
739  break;
740  case 'T':
741  drill_parse_T_code(fd, state, image);
742  break;
743  case 'V' :
744  gerb_ungetc (fd);
745  tmps = get_line (fd);
746  /* Silently ignore VER,1. Not sure what others are allowed */
747  if (strcmp (tmps, "VER,1") != 0) {
748  string = g_strdup_printf(_("Undefined header line = '%s'\n"),tmps);
749  drill_stats_add_error(stats->error_list,
750  -1,
751  g_strdup_printf(_("Undefined header line = '%s'\n"),tmps),
753  g_free(string);
754  }
755  g_free (tmps);
756  break;
757 
758  case 'X':
759  case 'Y':
760  /* Hole coordinate found. Do some parsing */
761  drill_parse_coordinate(fd, read, image, state);
762 
763  /* add the new drill hole */
764  curr_net = drill_add_drill_hole (image, state, stats, curr_net);
765  break;
766 
767  case '%':
768  state->curr_section = DRILL_DATA;
769  break;
770  case 10 : /* White space */
771  case 13 :
772  case ' ' :
773  case '\t' :
774  break;
775  default:
776  if(state->curr_section == DRILL_HEADER) {
777  /* Unrecognised crap in the header is thrown away */
778  drill_stats_add_error(stats->error_list,
779  -1,
780  _("Undefined codes found in header.\n"),
782  gerb_ungetc(fd);
783  tmps = get_line(fd);
784  string = g_strdup_printf(_("Undefined header line = '%s'\n"),
785  tmps);
786  drill_stats_add_error(stats->error_list,
787  -1,
788  string,
790  g_free(string);
791  g_free (tmps);
792  } else {
793  string = g_strdup_printf(_("Undefined character '%c' [0x%02x] found inside data, ignoring\n"),
794  read, read);
795  drill_stats_add_error(stats->error_list,
796  -1,
797  string,
799  g_free(string);
800  }
801  }
802  }
803  drill_stats_add_error(stats->error_list,
804  -1,
805  _("No EOF found in drill file.\n"),
807 
808  drill_parse_end:
809  dprintf ("%s(): Populating file attributes\n", __FUNCTION__);
810 
811  switch (state->unit) {
812  case GERBV_UNIT_MM:
813  image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_MM;
814  break;
815 
816  default:
817  image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_INCH;
818  break;
819  }
820 
821  switch (state->number_format) {
822  case FMT_000_00:
823  case FMT_0000_00:
824  image->info->attr_list[HA_digits].default_val.int_value = 2;
825  break;
826 
827  case FMT_000_000:
828  image->info->attr_list[HA_digits].default_val.int_value = 3;
829  break;
830 
831  case FMT_00_0000:
832  image->info->attr_list[HA_digits].default_val.int_value = 4;
833  break;
834 
835  case FMT_USER:
836  dprintf ("%s(): Keeping user specified number of decimal places (%d)\n",
837  __FUNCTION__,
838  image->info->attr_list[HA_digits].default_val.int_value);
839  break;
840 
841  default:
842  break;
843  }
844 
845  switch (image->format->omit_zeros) {
847  image->info->attr_list[HA_supression].default_val.int_value = SUP_LEAD;
848  break;
849 
851  image->info->attr_list[HA_supression].default_val.int_value = SUP_TRAIL;
852  break;
853 
854  default:
855  image->info->attr_list[HA_supression].default_val.int_value = SUP_NONE;
856  break;
857  }
858 
859  g_free(state);
860 
861  return image;
862 } /* parse_drillfile */
863 
864 
865 /* -------------------------------------------------------------- */
866 /*
867  * Checks for signs that this is a drill file
868  * Returns TRUE if it is, FALSE if not.
869  */
870 gboolean
871 drill_file_p(gerb_file_t *fd, gboolean *returnFoundBinary)
872 {
873  char *buf=NULL, *tbuf;
874  int len = 0;
875  char *letter;
876  int ascii;
877  int zero = 48; /* ascii 0 */
878  int nine = 57; /* ascii 9 */
879  int i;
880  gboolean found_binary = FALSE;
881  gboolean found_M48 = FALSE;
882  gboolean found_M30 = FALSE;
883  gboolean found_percent = FALSE;
884  gboolean found_T = FALSE;
885  gboolean found_X = FALSE;
886  gboolean found_Y = FALSE;
887  gboolean end_comments=FALSE;
888 
889  tbuf = g_malloc(MAXL);
890  if (tbuf == NULL)
891  GERB_FATAL_ERROR(_("malloc buf failed while checking for drill file."));
892 
893  while (fgets(tbuf, MAXL, fd->fd) != NULL) {
894  len = strlen(tbuf);
895  buf = tbuf;
896  /* check for comments at top of file. */
897  if(!end_comments){
898  if(g_strstr_len(buf, len, ";")!=NULL){/* comments at top of file */
899  for (i=0;i<len-1;++i){
900  if(buf[i]=='\n' && buf[i+1] != ';' && buf[i+1] != '\r' && buf[i+1] != '\n'){
901  end_comments=TRUE;
902  buf=&tbuf[i+1]; /* set rest of parser to end of comments */
903 
904  }
905  }
906  if(!end_comments)
907  continue;
908  }
909  else
910  end_comments=TRUE;
911  }
912 
913  /* First look through the file for indications of its type */
914  len = strlen(buf);
915  /* check that file is not binary (non-printing chars) */
916  for (i = 0; i < len; i++) {
917  ascii = (int) buf[i];
918  if ((ascii > 128) || (ascii < 0)) {
919  found_binary = TRUE;
920  }
921  }
922 
923  /* Check for M48 = start of drill header */
924  if (g_strstr_len(buf, len, "M48")) {
925  found_M48 = TRUE;
926  }
927 
928  /* Check for M30 = end of drill program */
929  if (g_strstr_len(buf, len, "M30")) {
930  if (found_percent) {
931  found_M30 = TRUE; /* Found M30 after % = good */
932  }
933  }
934 
935  /* Check for % on its own line at end of header */
936  if ((letter = g_strstr_len(buf, len, "%")) != NULL) {
937  if ((letter[1] == '\r') || (letter[1] == '\n'))
938  found_percent = TRUE;
939  }
940 
941  /* Check for T<number> */
942  if ((letter = g_strstr_len(buf, len, "T")) != NULL) {
943  if (!found_T && (found_X || found_Y)) {
944  found_T = FALSE; /* Found first T after X or Y */
945  } else {
946  if (isdigit( (int) letter[1])) { /* verify next char is digit */
947  found_T = TRUE;
948  }
949  }
950  }
951 
952  /* look for X<number> or Y<number> */
953  if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
954  ascii = (int) letter[1]; /* grab char after X */
955  if ((ascii >= zero) && (ascii <= nine)) {
956  found_X = TRUE;
957  }
958  }
959  if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
960  ascii = (int) letter[1]; /* grab char after Y */
961  if ((ascii >= zero) && (ascii <= nine)) {
962  found_Y = TRUE;
963  }
964  }
965  } /* while (fgets(buf, MAXL, fd->fd) */
966 
967  rewind(fd->fd);
968  g_free(tbuf);
969  *returnFoundBinary = found_binary;
970 
971  /* Now form logical expression determining if this is a drill file */
972  if ( ((found_X || found_Y) && found_T) &&
973  (found_M48 || (found_percent && found_M30)) )
974  return TRUE;
975  else if (found_M48 && found_T && found_percent && found_M30)
976  /* Pathological case of drill file with valid header
977  and EOF but no drill XY locations. */
978  return TRUE;
979  else
980  return FALSE;
981 } /* drill_file_p */
982 
983 
984 /* -------------------------------------------------------------- */
985 /* Parse tool definition. This can get a bit tricky since it can
986  appear in the header and/or data section.
987  Returns tool number on success, -1 on error */
988 static int
989 drill_parse_T_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
990 {
991  int tool_num;
992  gboolean done = FALSE;
993  int temp;
994  double size;
995  gerbv_drill_stats_t *stats = image->drill_stats;
996  gchar *tmps;
997  gchar *string;
998 
999  /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
1000  broken headers from Orcad, which is crap */
1001  temp = gerb_fgetc(fd);
1002  dprintf("Found a char %d after the T\n", temp);
1003 
1004  /* might be a tool tool change stop switch on/off*/
1005  if((temp == 'C') && ((fd->ptr + 2) < fd->datalen)){
1006  if(gerb_fgetc(fd) == 'S'){
1007  if (gerb_fgetc(fd) == 'T' ){
1008  fd->ptr -= 4;
1009  tmps = get_line(fd++);
1010  string = g_strdup_printf(_("Tool change stop switch found: %s\n"), tmps);
1011  drill_stats_add_error(stats->error_list,
1012  -1,
1013  string,
1015  g_free(string);
1016  g_free (tmps);
1017  return -1;
1018  }
1019  gerb_ungetc(fd);
1020  }
1021  gerb_ungetc(fd);
1022  }
1023 
1024  if( !(isdigit(temp) != 0 || temp == '+' || temp =='-') ) {
1025  if(temp != EOF) {
1026  drill_stats_add_error(stats->error_list,
1027  -1,
1028  _("Orcad bug: Junk text found in place of tool definition.\n"),
1030  tmps = get_line(fd);
1031  string = g_strdup_printf(_("Junk text = %s\n"),
1032  tmps);
1033  drill_stats_add_error(stats->error_list,
1034  -1,
1035  string,
1037  g_free(string);
1038  g_free (tmps);
1039  drill_stats_add_error(stats->error_list,
1040  -1,
1041  _("Ignorning junk text.\n"),
1043  }
1044  return -1;
1045  }
1046  gerb_ungetc(fd);
1047 
1048  tool_num = (int) gerb_fgetint(fd, NULL);
1049  dprintf ("In %s: handling tool_num = %d\n", __FUNCTION__, tool_num);
1050 
1051  if (tool_num == 0)
1052  return tool_num; /* T00 is a command to unload the drill */
1053 
1054  if ( (tool_num < TOOL_MIN) || (tool_num >= TOOL_MAX) ) {
1055  string = g_strdup_printf(_("Drill number out of bounds: %d.\n"), tool_num);
1056  drill_stats_add_error(stats->error_list,
1057  -1,
1058  string,
1060  g_free(string);
1061  }
1062 
1063  /* Set the current tool to the correct one */
1064  state->current_tool = tool_num;
1065 
1066  /* Check for a size definition */
1067  temp = gerb_fgetc(fd);
1068 
1069  /* This bit of code looks for a tool definition by scanning for strings
1070  * of form TxxC, TxxF, TxxS. */
1071  while(!done) {
1072 
1073  switch((char)temp) {
1074  case 'C':
1075  size = read_double(fd, state->header_number_format, GERBV_OMIT_ZEROS_TRAILING, state->decimals);
1076  dprintf ("%s: Read a size of %g %s\n", __FUNCTION__, size,
1077  state->unit == GERBV_UNIT_MM ? "mm" : "inch");
1078  if(state->unit == GERBV_UNIT_MM) {
1079  size /= 25.4;
1080  } else if(size >= 4.0) {
1081  /* If the drill size is >= 4 inches, assume that this
1082  must be wrong and that the units are mils.
1083  The limit being 4 inches is because the smallest drill
1084  I've ever seen used is 0,3mm(about 12mil). Half of that
1085  seemed a bit too small a margin, so a third it is */
1086  string = g_strdup_printf(_("Read a drill of diameter %g inches.\n"), size);
1087  drill_stats_add_error(stats->error_list,
1088  -1,
1089  string,
1091  g_free(string);
1092  string = g_strdup_printf(_("Assuming units are mils.\n"));
1093  drill_stats_add_error(stats->error_list,
1094  -1,
1095  string,
1097  g_free(string);
1098  size /= 1000.0;
1099  }
1100 
1101  if(size <= 0. || size >= 10000.) {
1102  string = g_strdup_printf(_("Unreasonable drill size found for drill %d: %g\n"), tool_num, size);
1103  drill_stats_add_error(stats->error_list,
1104  -1,
1105  string,
1107  g_free(string);
1108  } else {
1109  if(image->aperture[tool_num] != NULL) {
1110  /* allow a redefine of a tool only if the new definition is exactly the same.
1111  * This avoid lots of spurious complaints with the output of some cad
1112  * tools while keeping complaints if there is a true problem
1113  */
1114  if (image->aperture[tool_num]->parameter[0] != size ||
1115  image->aperture[tool_num]->type != GERBV_APTYPE_CIRCLE ||
1116  image->aperture[tool_num]->nuf_parameters != 1 ||
1117  image->aperture[tool_num]->unit != GERBV_UNIT_INCH) {
1118  string = g_strdup_printf(_("Found redefinition of drill %d.\n"), tool_num);
1119  drill_stats_add_error(stats->error_list,
1120  -1,
1121  string,
1123  g_free(string);
1124  }
1125  } else {
1126  image->aperture[tool_num] =
1127  (gerbv_aperture_t *)g_malloc0(sizeof(gerbv_aperture_t));
1128  if (image->aperture[tool_num] == NULL)
1129  GERB_FATAL_ERROR(_("malloc tool failed"));
1130 
1131  /* There's really no way of knowing what unit the tools
1132  are defined in without sneaking a peek in the rest of
1133  the file first. That's done in drill_guess_format() */
1134  image->aperture[tool_num]->parameter[0] = size;
1135  image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1136  image->aperture[tool_num]->nuf_parameters = 1;
1137  image->aperture[tool_num]->unit = GERBV_UNIT_INCH;
1138  }
1139  }
1140 
1141  /* Add the tool whose definition we just found into the list
1142  * of tools for this layer used to generate statistics. */
1143  stats = image->drill_stats;
1144  string = g_strdup_printf("%s", (state->unit == GERBV_UNIT_MM ? _("mm") : _("inch")));
1145  drill_stats_add_to_drill_list(stats->drill_list,
1146  tool_num,
1147  state->unit == GERBV_UNIT_MM ? size*25.4 : size,
1148  string);
1149  g_free(string);
1150  break;
1151 
1152  case 'F':
1153  case 'S' :
1154  /* Silently ignored. They're not important. */
1155  gerb_fgetint(fd, NULL);
1156  break;
1157 
1158  default:
1159  /* Stop when finding anything but what's expected
1160  (and put it back) */
1161  gerb_ungetc(fd);
1162  done = TRUE;
1163  break;
1164  } /* switch((char)temp) */
1165 
1166  if( (temp = gerb_fgetc(fd)) == EOF) {
1167  drill_stats_add_error(stats->error_list,
1168  -1,
1169  _("Unexpected EOF encountered header of drill file.\n"),
1171  }
1172  } /* while(!done) */ /* Done looking at tool definitions */
1173 
1174  /* Catch the tools that aren't defined.
1175  This isn't strictly a good thing, but at least something is shown */
1176  if(image->aperture[tool_num] == NULL) {
1177  double dia;
1178 
1179  image->aperture[tool_num] =
1180  (gerbv_aperture_t *)g_malloc0(sizeof(gerbv_aperture_t));
1181  if (image->aperture[tool_num] == NULL)
1182  GERB_FATAL_ERROR(_("malloc tool failed"));
1183 
1184  /* See if we have the tool table */
1185  dia = gerbv_get_tool_diameter(tool_num);
1186  if (dia <= 0) {
1187  /*
1188  * There is no tool. So go out and make some.
1189  * This size calculation is, of course, totally bogus.
1190  */
1191  dia = (double)(16 + 8 * tool_num) / 1000;
1192  /*
1193  * Oooh, this is sooo ugly. But some CAD systems seem to always
1194  * use T00 at the end of the file while others that don't have
1195  * tool definitions inside the file never seem to use T00 at all.
1196  */
1197  if(tool_num != 0) {
1198  string = g_strdup_printf(_("Tool %02d used without being defined\n"), tool_num);
1199  drill_stats_add_error(stats->error_list,
1200  -1,
1201  string,
1203  g_free(string);
1204  string = g_strdup_printf(_("Setting a default size of %g\"\n"), dia);
1205  drill_stats_add_error(stats->error_list,
1206  -1,
1207  string,
1209  g_free(string);
1210  }
1211  }
1212 
1213  image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
1214  image->aperture[tool_num]->nuf_parameters = 1;
1215  image->aperture[tool_num]->parameter[0] = dia;
1216 
1217  /* Add the tool whose definition we just found into the list
1218  * of tools for this layer used to generate statistics. */
1219  if (tool_num != 0) { /* Only add non-zero tool nums.
1220  * Zero = unload command. */
1221  stats = image->drill_stats;
1222  string = g_strdup_printf("%s",
1223  (state->unit == GERBV_UNIT_MM ? _("mm") : _("inch")));
1224  drill_stats_add_to_drill_list(stats->drill_list,
1225  tool_num,
1226  state->unit == GERBV_UNIT_MM ? dia*25.4 : dia,
1227  string);
1228  g_free(string);
1229  }
1230  } /* if(image->aperture[tool_num] == NULL) */
1231 
1232  return tool_num;
1233 } /* drill_parse_T_code */
1234 
1235 
1236 /* -------------------------------------------------------------- */
1237 static drill_m_code_t
1238 drill_parse_M_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
1239 {
1240  char op[3] = " ";
1241  int read[3];
1242  gerbv_drill_stats_t *stats = image->drill_stats;
1243  drill_m_code_t result = DRILL_M_UNKNOWN;
1244 
1245  dprintf("---> entering drill_parse_M_code ...\n");
1246 
1247  read[0] = gerb_fgetc(fd);
1248  read[1] = gerb_fgetc(fd);
1249 
1250  if ((read[0] == EOF) || (read[1] == EOF))
1251  drill_stats_add_error(stats->error_list,
1252  -1,
1253  _("Unexpected EOF found while parsing M code.\n"),
1255  op[0] = read[0], op[1] = read[1], op[2] = 0;
1256 
1257  if (strncmp(op, "00", 2) == 0) {
1258  stats->M00++;
1259  result = DRILL_M_END;
1260  } else if (strncmp(op, "01", 2) == 0) {
1261  stats->M01++;
1262  result = DRILL_M_ENDPATTERN;
1263  } else if (strncmp(op, "18", 2) == 0) {
1264  stats->M18++;
1265  result = DRILL_M_TIPCHECK;
1266  } else if (strncmp(op, "25", 2) == 0) {
1267  stats->M25++;
1268  result = DRILL_M_BEGINPATTERN;
1269  } else if (strncmp(op, "31", 2) == 0) {
1270  stats->M31++;
1271  result = DRILL_M_BEGINPATTERN;
1272  } else if (strncmp(op, "30", 2) == 0) {
1273  stats->M30++;
1274  result = DRILL_M_ENDREWIND;
1275  } else if (strncmp(op, "45", 2) == 0) {
1276  stats->M45++;
1277  result = DRILL_M_LONGMESSAGE;
1278  } else if (strncmp(op, "47", 2) == 0) {
1279  stats->M47++;
1280  result = DRILL_M_MESSAGE;
1281  } else if (strncmp(op, "48", 2) == 0) {
1282  stats->M48++;
1283  result = DRILL_M_HEADER;
1284  } else if (strncmp(op, "71", 2) == 0) {
1285  eat_line(fd);
1286  stats->M71++;
1287  result = DRILL_M_METRIC;
1288  } else if (strncmp(op, "72", 2) == 0) {
1289  eat_line(fd);
1290  stats->M72++;
1291  result = DRILL_M_IMPERIAL;
1292  } else if (strncmp(op, "95", 2) == 0) {
1293  stats->M95++;
1294  result = DRILL_M_ENDHEADER;
1295  } else if (strncmp(op, "97", 2) == 0) {
1296  stats->M97++;
1297  result = DRILL_M_CANNEDTEXT;
1298  } else if (strncmp(op, "98", 2) == 0) {
1299  stats->M98++;
1300  return DRILL_M_CANNEDTEXT;
1301  } else if (state->curr_section == DRILL_HEADER &&
1302  strncmp(op, "ET", 2) == 0) {
1303  /* METRIC is not an actual M code but a command that is only
1304  acceptable within the header.
1305 
1306  The syntax is
1307  METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
1308  */
1309  if ('R' == gerb_fgetc(fd) &&
1310  'I' == gerb_fgetc(fd) &&
1311  'C' == gerb_fgetc(fd)) {
1312  again:
1313  if (',' == gerb_fgetc(fd)) {
1314  int c;
1315 
1316  /* Is it tzlz, or zerofmt? */
1317  switch ((c = gerb_fgetc(fd))) {
1318  case 'T':
1319  case 'L':
1320  if ('Z' != gerb_fgetc(fd))
1321  goto junk;
1322  if (c == 'L')
1323  {
1324  dprintf ("%s(): Detected a file that probably has trailing zero supression\n", __FUNCTION__);
1325  if (state->autod)
1326  {
1327  image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1328  }
1329  }
1330  else
1331  {
1332  dprintf ("%s(): Detected a file that probably has leading zero supression\n", __FUNCTION__);
1333  if (state->autod)
1334  {
1335  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1336  }
1337  }
1338  if (state->autod)
1339  {
1340  /* Default metric number format is 6-digit, 1 um
1341  resolution. The header number format (for T#C#
1342  definitions) is fixed to that, while the number
1343  format within the file can differ. */
1344  state->header_number_format =
1345  state->number_format = FMT_000_000;
1346  state->decimals = 3;
1347  }
1348  c = gerb_fgetc(fd);
1349  gerb_ungetc(fd);
1350  if (c == ',')
1351  /* anticipate number format will follow */
1352  goto again;
1353  break;
1354 
1355  case '0':
1356  if ('0' != gerb_fgetc(fd) ||
1357  '0' != gerb_fgetc(fd))
1358  goto junk;
1359  /* We just parsed three 0s, the remainder options
1360  so far are: .000 | .00 | 0.00 */
1361  read[0] = gerb_fgetc(fd);
1362  read[1] = gerb_fgetc(fd);
1363  if (read[0] == EOF || read[1] == EOF)
1364  goto junk;
1365  op[0] = read[0];
1366  op[1] = read[1];
1367  if (strcmp(op, "0.") == 0) {
1368  /* expecting FMT_0000_00,
1369  two trailing 0s must follow */
1370  if ('0' != gerb_fgetc(fd) ||
1371  '0' != gerb_fgetc(fd))
1372  goto junk;
1373  eat_line(fd);
1374  if (state->autod)
1375  {
1376  state->number_format = FMT_0000_00;
1377  state->decimals = 2;
1378  }
1379  break;
1380  }
1381  if (strcmp(op, ".0") != 0)
1382  goto junk;
1383  /* must be either FMT_000_000 or FMT_000_00, depending
1384  on whether one or two 0s are following */
1385  if ('0' != gerb_fgetc(fd))
1386  goto junk;
1387  if ('0' == gerb_fgetc(fd) && state->autod)
1388  {
1389  state->number_format = FMT_000_000;
1390  state->decimals = 3;
1391  }
1392  else {
1393  gerb_ungetc(fd);
1394  if (state->autod)
1395  {
1396  state->number_format = FMT_000_00;
1397  state->decimals = 2;
1398  }
1399  }
1400  eat_line(fd);
1401  break;
1402 
1403  default:
1404  junk:
1405  drill_stats_add_error(stats->error_list,
1406  -1,
1407  _("Found junk after METRIC command\n"),
1409  gerb_ungetc(fd);
1410  eat_line(fd);
1411  break;
1412  }
1413  } else {
1414  gerb_ungetc(fd);
1415  eat_line(fd);
1416  }
1417 
1418  return DRILL_M_METRICHEADER;
1419  }
1420  } else {
1421  stats->M_unknown++;
1422  result = DRILL_M_UNKNOWN;
1423  }
1424 
1425  dprintf("<---- ...leaving drill_parse_M_code.\n");
1426  return result;
1427 } /* drill_parse_M_code */
1428 
1429 
1430 /* -------------------------------------------------------------- */
1431 static drill_g_code_t
1432 drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image)
1433 {
1434  char op[3] = " ";
1435  int read[3];
1436  gerbv_drill_stats_t *stats = image->drill_stats;
1437  drill_g_code_t result;
1438 
1439  dprintf("---> entering drill_parse_G_code ...\n");
1440 
1441  read[0] = gerb_fgetc(fd);
1442  read[1] = gerb_fgetc(fd);
1443 
1444  if ((read[0] == EOF) || (read[1] == EOF)) {
1445  drill_stats_add_error(stats->error_list,
1446  -1,
1447  _("Unexpected EOF found while parsing G code.\n"),
1449  }
1450 
1451  op[0] = read[0], op[1] = read[1], op[2] = 0;
1452 
1453  if (strncmp(op, "00", 2) == 0) {
1454  stats->G00++;
1455  result = DRILL_G_ROUT;
1456  } else if (strncmp(op, "01", 2) == 0) {
1457  stats->G01++;
1458  result = DRILL_G_LINEARMOVE;
1459  } else if (strncmp(op, "02", 2) == 0) {
1460  stats->G02++;
1461  result = DRILL_G_CWMOVE;
1462  } else if (strncmp(op, "03", 2) == 0) {
1463  stats->G03++;
1464  result = DRILL_G_CCWMOVE;
1465  } else if (strncmp(op, "05", 2) == 0) {
1466  stats->G05++;
1467  result = DRILL_G_DRILL;
1468  } else if (strncmp(op, "85", 2) == 0) {
1469  stats->G85++;
1470  result = DRILL_G_SLOT;
1471  } else if (strncmp(op, "90", 2) == 0) {
1472  stats->G90++;
1473  result = DRILL_G_ABSOLUTE;
1474  } else if (strncmp(op, "91", 2) == 0) {
1475  stats->G91++;
1476  result = DRILL_G_INCREMENTAL;
1477  } else if (strncmp(op, "93", 2) == 0) {
1478  stats->G93++;
1479  result = DRILL_G_ZEROSET;
1480  } else {
1481  stats->G_unknown++;
1482  result = DRILL_G_UNKNOWN;
1483  }
1484 
1485  dprintf("<---- ...leaving drill_parse_G_code.\n");
1486  return result;
1487 
1488 } /* drill_parse_G_code */
1489 
1490 
1491 /* -------------------------------------------------------------- */
1492 /* Parse on drill file coordinate.
1493  Returns nothing, but modifies state */
1494 static void
1495 drill_parse_coordinate(gerb_file_t *fd, char firstchar,
1496  gerbv_image_t *image, drill_state_t *state)
1497 {
1498  int read;
1499 
1500  if(state->coordinate_mode == DRILL_MODE_ABSOLUTE) {
1501  if(firstchar == 'X') {
1502  state->curr_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1503  if((read = (char)gerb_fgetc(fd)) == 'Y') {
1504  state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1505  }
1506  } else {
1507  state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1508  }
1509  } else if(state->coordinate_mode == DRILL_MODE_INCREMENTAL) {
1510  if(firstchar == 'X') {
1511  state->curr_x += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1512  if((read = (char)gerb_fgetc(fd)) == 'Y') {
1513  state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1514  }
1515  } else {
1516  state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
1517  }
1518  }
1519 
1520 } /* drill_parse_coordinate */
1521 
1522 
1523 /* Allocates and returns a new drill_state structure
1524  Returns state pointer on success, NULL on ERROR */
1525 static drill_state_t *
1526 new_state(drill_state_t *state)
1527 {
1528  state = (drill_state_t *)g_malloc0(sizeof(drill_state_t));
1529  if (state != NULL) {
1530  /* Init structure */
1531  state->curr_section = DRILL_NONE;
1532  state->coordinate_mode = DRILL_MODE_ABSOLUTE;
1533  state->origin_x = 0.0;
1534  state->origin_y = 0.0;
1535  state->unit = GERBV_UNIT_UNSPECIFIED;
1536  state->backup_number_format = FMT_000_000; /* only used for METRIC */
1537  state->header_number_format = state->number_format = FMT_00_0000; /* i. e. INCH */
1538  state->autod = 1;
1539  state->decimals = 4;
1540  }
1541 
1542  return state;
1543 } /* new_state */
1544 
1545 
1546 /* -------------------------------------------------------------- */
1547 /* Reads one double from fd and returns it.
1548  If a decimal point is found, fmt is not used. */
1549 static double
1550 read_double(gerb_file_t *fd, number_fmt_t fmt, gerbv_omit_zeros_t omit_zeros, int decimals)
1551 {
1552  int read;
1553  char temp[DRILL_READ_DOUBLE_SIZE];
1554  int i = 0, ndigits = 0;
1555  double result;
1556  gboolean decimal_point = FALSE;
1557  gboolean sign_prepend = FALSE;
1558 
1559  dprintf("%s(%p, %d, %d, %d)\n", __FUNCTION__, fd, fmt, omit_zeros, decimals);
1560 
1561  memset(temp, 0, sizeof(temp));
1562 
1563  read = gerb_fgetc(fd);
1564  while(read != EOF && i < (DRILL_READ_DOUBLE_SIZE -1) &&
1565  (isdigit(read) || read == '.' || read == ',' || read == '+' || read == '-')) {
1566  if(read == ',' || read == '.') decimal_point = TRUE;
1567 
1568  /*
1569  * FIXME -- if we are going to do this, don't we need a
1570  * locale-independent strtod()? I think pcb has one.
1571  */
1572  if(read == ',')
1573  read = '.'; /* adjust for strtod() */
1574 
1575  if(isdigit(read)) ndigits++;
1576 
1577  if(read == '-' || read == '+')
1578  sign_prepend = TRUE;
1579 
1580  temp[i++] = (char)read;
1581  read = gerb_fgetc(fd);
1582  }
1583  temp[i] = 0;
1584 
1585  gerb_ungetc(fd);
1586  if (decimal_point) {
1587  result = strtod(temp, NULL);
1588  } else {
1589  int wantdigits;
1590  double scale;
1591  char tmp2[DRILL_READ_DOUBLE_SIZE];
1592 
1593  memset(tmp2, 0, sizeof(tmp2));
1594 
1595  dprintf("%s(): omit_zeros = %d, fmt = %d\n", __FUNCTION__, omit_zeros, fmt);
1596  /* Nothing to take care for when leading zeros are
1597  omitted. */
1598  if (omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
1599  switch (fmt) {
1600  case FMT_00_0000:
1601  wantdigits = 2;
1602  break;
1603 
1604  case FMT_000_000:
1605  wantdigits = 3;
1606  break;
1607 
1608  case FMT_0000_00:
1609  wantdigits = 4;
1610  break;
1611 
1612  case FMT_000_00:
1613  wantdigits = 3;
1614  break;
1615 
1616  case FMT_USER:
1617  wantdigits = decimals;
1618  break;
1619 
1620  default:
1621  /* cannot happen, just plugs a compiler warning */
1622  fprintf(stderr, _("%s(): omit_zeros == GERBV_OMIT_ZEROS_TRAILING but fmt = %d.\n"
1623  "This should never have happened\n"), __FUNCTION__, fmt);
1624  return 0;
1625  }
1626 
1627  /* need to add an extra char for '+' or '-' */
1628  if (sign_prepend)
1629  wantdigits++;
1630 
1631 
1632  /*
1633  * we need at least wantdigits + one for the decimal place
1634  * + one for the terminating null character
1635  */
1636  if (wantdigits > sizeof(tmp2) - 2) {
1637  fprintf(stderr, _("%s(): wantdigits = %d which exceeds the maximum allowed size\n"),
1638  __FUNCTION__, wantdigits);
1639  return 0;
1640  }
1641 
1642  /*
1643  * After we have read the correct number of digits
1644  * preceeding the decimal point, insert a decimal point
1645  * and append the rest of the digits.
1646  */
1647  dprintf("%s(): wantdigits = %d, strlen(\"%s\") = %ld\n",
1648  __FUNCTION__, wantdigits, temp, (long) strlen(temp));
1649  for (i = 0 ; i < wantdigits && i < strlen(temp) ; i++) {
1650  tmp2[i] = temp[i];
1651  }
1652  for ( ; i < wantdigits ; i++) {
1653  tmp2[i] = '0';
1654  }
1655  tmp2[i++] = '.';
1656  for ( ; i <= strlen(temp) ; i++) {
1657  tmp2[i] = temp[i-1];
1658  }
1659  dprintf("%s(): After dealing with trailing zero supression, convert \"%s\"\n", __FUNCTION__, tmp2);
1660  scale = 1.0;
1661 
1662  for (i = 0 ; i <= strlen(tmp2) && i < sizeof (temp) ; i++) {
1663  temp[i] = tmp2[i];
1664  }
1665 
1666  } else {
1667 
1668  /*
1669  * figure out the scale factor when we are not suppressing
1670  * trailing zeros.
1671  */
1672  switch (fmt) {
1673  case FMT_00_0000:
1674  scale = 1E-4;
1675  break;
1676 
1677  case FMT_000_000:
1678  scale = 1E-3;
1679  break;
1680 
1681  case FMT_000_00:
1682  case FMT_0000_00:
1683  scale = 1E-2;
1684  break;
1685 
1686  case FMT_USER:
1687  scale = pow (10.0, -1.0*decimals);
1688  break;
1689 
1690  default:
1691  /* cannot happen, just plugs a compiler warning */
1692  fprintf (stderr, _("%s(): Unhandled fmt ` %d\n"), __FUNCTION__, fmt);
1693  exit (1);
1694  }
1695  }
1696 
1697  result = strtod(temp, NULL) * scale;
1698  }
1699 
1700  return result;
1701 } /* read_double */
1702 
1703 
1704 /* -------------------------------------------------------------- */
1705 /* Eats all characters up to and including
1706  the first one of CR or LF */
1707 static void
1708 eat_line(gerb_file_t *fd)
1709 {
1710  int read = gerb_fgetc(fd);
1711 
1712  while(read != 10 && read != 13) {
1713  if (read == EOF) return;
1714  read = gerb_fgetc(fd);
1715  }
1716 } /* eat_line */
1717 
1718 /* -------------------------------------------------------------- */
1719 static char *
1720 get_line(gerb_file_t *fd)
1721 {
1722  int read = gerb_fgetc(fd);
1723  gchar *retstring;
1724  gchar *tmps=g_strdup("");
1725 
1726  while(read != 10 && read != 13) {
1727  if (read == EOF)
1728  return tmps;
1729  retstring = g_strdup_printf("%s%c", tmps, read);
1730 
1731  /* since g_strdup_printf allocates memory, we need to free it */
1732  if (tmps) {
1733  g_free (tmps);
1734  tmps = NULL;
1735  }
1736  tmps = retstring;;
1737  read = gerb_fgetc(fd);
1738  }
1739  return tmps;
1740 } /* get_line */
1741