2 * gEDA - GNU Electronic Design Automation
4 * Copyright (C) 2000-2006 Andreas Andersson
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.
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.
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
24 \brief Excellon drill parsing functions
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
47 #include <math.h> /* pow() */
50 #include <sys/types.h>
57 #include "attribute.h"
61 #include "drill_stats.h"
65 /* DEBUG printing. #define DEBUG 1 in config.h to use this fcn. */
66 #define dprintf if(DEBUG) printf
68 #define NOT_IMPL(fd, s) do { \
69 GERB_MESSAGE("Not Implemented:%s\n", s); \
73 #define DRILL_READ_DOUBLE_SIZE 32
76 #define max(a,b) ((a) > (b) ? (a) : (b))
78 #define min(a,b) ((a) < (b) ? (a) : (b))
80 enum drill_file_section_t
{DRILL_NONE
, DRILL_HEADER
, DRILL_DATA
};
81 enum drill_coordinate_mode_t
{DRILL_MODE_ABSOLUTE
, DRILL_MODE_INCREMENTAL
};
83 enum drill_m_code_t
{DRILL_M_UNKNOWN
, DRILL_M_NOT_IMPLEMENTED
,
84 DRILL_M_END
, DRILL_M_ENDREWIND
,
85 DRILL_M_MESSAGE
, DRILL_M_LONGMESSAGE
,
86 DRILL_M_HEADER
, DRILL_M_ENDHEADER
,
87 DRILL_M_METRIC
, DRILL_M_IMPERIAL
,
88 DRILL_M_BEGINPATTERN
, DRILL_M_ENDPATTERN
,
89 DRILL_M_CANNEDTEXT
, DRILL_M_TIPCHECK
,
90 DRILL_M_METRICHEADER
, DRILL_M_IMPERIALHEADER
};
93 enum drill_g_code_t
{DRILL_G_ABSOLUTE
, DRILL_G_INCREMENTAL
,
94 DRILL_G_ZEROSET
, DRILL_G_UNKNOWN
,
95 DRILL_G_ROUT
, DRILL_G_DRILL
,
96 DRILL_G_LINEARMOVE
, DRILL_G_CWMOVE
, DRILL_G_CCWMOVE
};
98 enum number_fmt_t
{FMT_00_0000
/* INCH */,
99 FMT_000_000
/* METRIC 6-digit, 1 um */,
100 FMT_000_00
/* METRIC 5-digit, 10 um */,
101 FMT_0000_00
/* METRIC 6-digit, 10 um */,
102 FMT_USER
/* User defined format */};
104 typedef struct drill_state
{
113 /* number_format is used throughout the file itself.
115 header_number_format is used to parse the tool definition C
116 codes within the header. It is fixed to FMT_00_0000 for INCH
117 measures, and FMT_000_000 (1 um resolution) for metric
119 enum number_fmt_t number_format
, header_number_format
;
120 /* Used as a backup when temporarily switching to INCH. */
121 enum number_fmt_t backup_number_format
;
123 /* 0 means we don't try to autodetect any of the other values */
126 /* in FMT_USER this specifies the number of digits before the
127 * decimal point when doing trailing zero supression. Otherwise
128 * it is the number of digits *after* the decimal
135 /* Local function prototypes */
136 static int drill_parse_G_code(gerb_file_t
*fd
, gerbv_image_t
*image
);
137 static int drill_parse_M_code(gerb_file_t
*fd
, drill_state_t
*state
,
138 gerbv_image_t
*image
);
139 static int drill_parse_T_code(gerb_file_t
*fd
, drill_state_t
*state
,
140 gerbv_image_t
*image
);
141 static void drill_parse_coordinate(gerb_file_t
*fd
, char firstchar
,
142 gerbv_image_t
*image
, drill_state_t
*state
);
143 static drill_state_t
*new_state(drill_state_t
*state
);
144 static double read_double(gerb_file_t
*fd
, enum number_fmt_t fmt
,
145 gerbv_omit_zeros_t omit_zeros
, int decimals
);
146 static void eat_line(gerb_file_t
*fd
);
147 static char *get_line(gerb_file_t
*fd
);
149 /* -------------------------------------------------------------- */
150 /* This is the list of specific attributes a drill file may have from
151 * the point of view of parsing it.
154 static const char *supression_list
[] = {
164 static const char *units_list
[] = {
167 /* we don't do anything with mil yet so don't offer it as an
177 static gerbv_HID_Attribute drill_attribute_list
[] = {
178 /* This should be first */
179 {"autodetect", "Try to autodetect the file format",
180 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
183 {"zero_supression", "Zero supression",
184 HID_Enum
, 0, 0, {0, 0, 0}, supression_list
, 0},
185 #define HA_supression 1
188 HID_Enum
, 0, 0, {0, 0, 0}, units_list
, 0},
189 #define HA_xy_units 2
192 {"tool_units", "Tool size units",
193 HID_Enum
, 0, 0, {0, 0, 0}, units_list
, 0},
194 #define HA_tool_units 3
197 {"digits", "Number of digits. For trailing zero supression,"
198 " this is the number of digits before the decimal point. "
199 "Otherwise this is the number of digits after the decimal point.",
200 HID_Integer
, 0, 20, {5, 0, 0}, 0, 0},
206 drill_attribute_merge (gerbv_HID_Attribute
*dest
, int ndest
, gerbv_HID_Attribute
*src
, int nsrc
)
210 /* Here is a brain dead merge algorithm which shold make anyone cringe.
211 * Still, it is simple and we won't merge many attributes and not
215 for (i
= 0 ; i
< nsrc
; i
++) {
216 /* see if our destination wants this attribute */
218 while (j
< ndest
&& strcmp (src
[i
].name
, dest
[j
].name
) != 0)
221 /* if we wanted it and it is the same type, copy it over */
222 if (j
< ndest
&& src
[i
].type
== dest
[j
].type
) {
223 dest
[j
].default_val
= src
[i
].default_val
;
230 * Adds the actual drill hole to the drawing
233 drill_add_drill_hole (gerbv_image_t
*image
, drill_state_t
*state
, gerbv_drill_stats_t
*stats
, gerbv_net_t
*curr_net
)
235 /* Add one to drill stats for the current tool */
236 drill_stats_increment_drill_counter(image
->drill_stats
->drill_list
,
237 state
->current_tool
);
239 curr_net
->next
= (gerbv_net_t
*)g_malloc(sizeof(gerbv_net_t
));
240 if (curr_net
->next
== NULL
)
241 GERB_FATAL_ERROR("malloc curr_net->next failed\n");
242 curr_net
= curr_net
->next
;
243 memset((void *)curr_net
, 0, sizeof(gerbv_net_t
));
244 curr_net
->layer
= image
->layers
;
245 curr_net
->state
= image
->states
;
246 curr_net
->start_x
= (double)state
->curr_x
;
247 curr_net
->start_y
= (double)state
->curr_y
;
248 /* KLUDGE. This function isn't allowed to return anything
250 if(state
->unit
== GERBV_UNIT_MM
) {
251 curr_net
->start_x
/= 25.4;
252 curr_net
->start_y
/= 25.4;
253 /* KLUDGE. All images, regardless of input format,
254 are returned in INCH format */
255 curr_net
->state
->unit
= GERBV_UNIT_INCH
;
258 curr_net
->stop_x
= curr_net
->start_x
- state
->origin_x
;
259 curr_net
->stop_y
= curr_net
->start_y
- state
->origin_y
;
260 curr_net
->aperture
= state
->current_tool
;
261 curr_net
->aperture_state
= GERBV_APERTURE_STATE_FLASH
;
263 /* Find min and max of image.
264 Mustn't forget (again) to add the hole radius */
266 /* Check if aperture is set. Ignore the below instead of
268 if(image
->aperture
[state
->current_tool
] == NULL
)
271 curr_net
->boundingBox
.left
=curr_net
->start_x
-
272 image
->aperture
[state
->current_tool
]->parameter
[0] / 2;
273 curr_net
->boundingBox
.right
=curr_net
->start_x
+
274 image
->aperture
[state
->current_tool
]->parameter
[0] / 2;
275 curr_net
->boundingBox
.bottom
=curr_net
->start_y
-
276 image
->aperture
[state
->current_tool
]->parameter
[0] / 2;
277 curr_net
->boundingBox
.top
=curr_net
->start_y
+
278 image
->aperture
[state
->current_tool
]->parameter
[0] / 2;
281 min(image
->info
->min_x
,
283 image
->aperture
[state
->current_tool
]->parameter
[0] / 2));
285 min(image
->info
->min_y
,
287 image
->aperture
[state
->current_tool
]->parameter
[0] / 2));
289 max(image
->info
->max_x
,
291 image
->aperture
[state
->current_tool
]->parameter
[0] / 2));
293 max(image
->info
->max_y
,
295 image
->aperture
[state
->current_tool
]->parameter
[0] / 2));
300 /* -------------------------------------------------------------- */
302 parse_drillfile(gerb_file_t
*fd
, gerbv_HID_Attribute
*attr_list
, int n_attr
, int reload
)
304 drill_state_t
*state
= NULL
;
305 gerbv_image_t
*image
= NULL
;
306 gerbv_net_t
*curr_net
= NULL
;
308 gerbv_drill_stats_t
*stats
;
313 * many locales redefine "." as "," and so on, so sscanf and strtod
314 * has problems when reading files using %f format.
315 * Fixes bug #1963618 reported by Lorenzo Marcantonio.
317 setlocale(LC_NUMERIC
, "C" );
319 /* Create new image for this layer */
320 dprintf("In parse_drillfile, about to create image for this layer\n");
322 image
= gerbv_create_image(image
, "Excellon Drill File");
324 GERB_FATAL_ERROR("malloc image failed\n");
326 if (reload
&& attr_list
!= NULL
) {
327 /* FIXME there should probably just be a function to copy an
328 attribute list including using strdup as needed */
330 image
->info
->n_attr
= n_attr
;
331 image
->info
->attr_list
= gerbv_attribute_dup(attr_list
, n_attr
);
334 /* Copy in the default attribute list for drill files. We make a
335 * copy here because we will allow per-layer editing of the
338 image
->info
->n_attr
= sizeof (drill_attribute_list
) / sizeof (drill_attribute_list
[0]);
339 image
->info
->attr_list
= gerbv_attribute_dup (drill_attribute_list
, image
->info
->n_attr
);
341 /* now merge any project attributes */
342 drill_attribute_merge (image
->info
->attr_list
, image
->info
->n_attr
,
346 curr_net
= image
->netlist
;
347 curr_net
->layer
= image
->layers
;
348 curr_net
->state
= image
->states
;
349 image
->layertype
= GERBV_LAYERTYPE_DRILL
;
350 stats
= gerbv_drill_stats_new();
352 GERB_FATAL_ERROR("malloc stats failed\n");
353 image
->drill_stats
= stats
;
355 /* Create local state variable to track photoplotter state */
356 state
= new_state(state
);
358 GERB_FATAL_ERROR("malloc state failed\n");
360 image
->format
= (gerbv_format_t
*)g_malloc(sizeof(gerbv_format_t
));
361 if (image
->format
== NULL
)
362 GERB_FATAL_ERROR("malloc format failed\n");
363 memset((void *)image
->format
, 0, sizeof(gerbv_format_t
));
364 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_UNSPECIFIED
;
367 if (!image
->info
->attr_list
[HA_auto
].default_val
.int_value
) {
369 state
->number_format
= FMT_USER
;
370 state
->decimals
= image
->info
->attr_list
[HA_digits
].default_val
.int_value
;
371 if (image
->info
->attr_list
[HA_xy_units
].default_val
.int_value
== UNITS_MM
)
372 state
->unit
= GERBV_UNIT_MM
;
373 switch (image
->info
->attr_list
[HA_supression
].default_val
.int_value
) {
375 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_LEADING
;
379 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_TRAILING
;
383 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_EXPLICIT
;
388 dprintf("%s(): Starting parsing of drill file\n", __FUNCTION__
);
389 while ((read
= gerb_fgetc(fd
)) != EOF
) {
391 switch ((char) read
) {
393 /* Comment found. Eat rest of line */
398 tmps
= get_line (fd
);
399 if (strcmp (tmps
, "DETECT,ON") == 0 ||
400 strcmp (tmps
, "DETECT,OFF") == 0) {
403 if (strcmp (tmps
, "DETECT,ON") == 0)
408 /* broken tool detect on/off. Silently ignored. */
410 tmps2
= g_strdup_printf ("%s\n%s", stats
->detect
, tmps3
);
411 g_free (stats
->detect
);
413 tmps2
= g_strdup_printf ("%s", tmps3
);
415 stats
->detect
= tmps2
;
417 string
= g_strdup_printf("Undefined header line = '%s'\n",tmps
);
418 drill_stats_add_error(stats
->error_list
,
428 tmps
= get_line (fd
);
429 /* Silently ignore FMAT,2. Not sure what others are allowed */
430 if (strcmp (tmps
, "FMAT,2") != 0) {
431 string
= g_strdup_printf("Undefined header line = '%s'\n",tmps
);
432 drill_stats_add_error(stats
->error_list
,
442 /* Most G codes aren't used, for now */
443 switch(drill_parse_G_code(fd
, image
)) {
445 drill_stats_add_error(stats
->error_list
,
447 "Rout mode data is not supported\n",
448 GERBV_MESSAGE_ERROR
);
452 case DRILL_G_ABSOLUTE
:
453 state
->coordinate_mode
= DRILL_MODE_ABSOLUTE
;
455 case DRILL_G_INCREMENTAL
:
456 state
->coordinate_mode
= DRILL_MODE_INCREMENTAL
;
458 case DRILL_G_ZEROSET
:
459 if((read
= gerb_fgetc(fd
)) == EOF
)
460 drill_stats_add_error(stats
->error_list
,
462 "Unexpected EOF found.\n",
463 GERBV_MESSAGE_ERROR
);
464 drill_parse_coordinate(fd
, (char)read
, image
, state
);
465 state
->origin_x
= state
->curr_x
;
466 state
->origin_y
= state
->curr_y
;
474 if (state
->curr_section
!= DRILL_HEADER
)
477 int c
= gerb_fgetc(fd
);
480 if ('C' == gerb_fgetc(fd
)) {
481 if ('H' == gerb_fgetc(fd
)) {
482 state
->unit
= GERBV_UNIT_INCH
;
485 if (',' == gerb_fgetc(fd
)) {
487 if (c
!= EOF
&& 'Z' == gerb_fgetc(fd
)) {
491 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_TRAILING
;
492 state
->header_number_format
=
493 state
->number_format
= FMT_00_0000
;
500 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_LEADING
;
501 state
->header_number_format
=
502 state
->number_format
= FMT_00_0000
;
508 drill_stats_add_error(stats
->error_list
,
510 "Found junk after INCH command\n",
511 GERBV_MESSAGE_WARNING
);
515 drill_stats_add_error(stats
->error_list
,
517 "Found junk after INCH command\n",
518 GERBV_MESSAGE_WARNING
);
522 /* unget the char in case we just advanced past a new line char */
528 if ('I' == gerb_fgetc(fd
))
529 if (',' == gerb_fgetc(fd
))
530 if ('O' == gerb_fgetc(fd
)) {
531 if ('N' == (c
= gerb_fgetc(fd
)))
532 state
->coordinate_mode
= DRILL_MODE_INCREMENTAL
;
533 else if ('F' == c
) if ('F' == gerb_fgetc(fd
))
534 state
->coordinate_mode
= DRILL_MODE_ABSOLUTE
;
543 switch(drill_parse_M_code(fd
, state
, image
)) {
544 case DRILL_M_HEADER
:
545 state
->curr_section
= DRILL_HEADER
;
547 case DRILL_M_ENDHEADER
:
548 state
->curr_section
= DRILL_DATA
;
550 if (image
->format
->omit_zeros
== GERBV_OMIT_ZEROS_UNSPECIFIED
) {
551 /* Excellon says they default to specify leading
552 zeros, i.e. omit trailing zeros. The Excellon
553 files floating around that don't specify the
554 leading/trailing zeros in the header seem to
555 contradict to this though.
557 XXX We should probably ask the user. */
559 drill_stats_add_error(stats
->error_list
,
561 "End of Excellon header reached but no leading/trailing zero handling specified.\n",
562 GERBV_MESSAGE_ERROR
);
563 drill_stats_add_error(stats
->error_list
,
565 "Assuming leading zeros.\n",
566 GERBV_MESSAGE_WARNING
);
567 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_LEADING
;
570 case DRILL_M_METRIC
:
571 if (state
->unit
== GERBV_UNIT_UNSPECIFIED
&&
572 state
->curr_section
!= DRILL_HEADER
) {
573 drill_stats_add_error(stats
->error_list
,
575 "M71 code found but no METRIC specification in header.\n",
576 GERBV_MESSAGE_ERROR
);
577 drill_stats_add_error(stats
->error_list
,
579 "Assuming all tool sizes are MM.\n",
580 GERBV_MESSAGE_WARNING
);
583 stats
= image
->drill_stats
;
584 for (tool_num
= TOOL_MIN
; tool_num
< TOOL_MAX
; tool_num
++) {
585 if (image
->aperture
&& image
->aperture
[tool_num
]) {
586 /* First update stats. Do this before changing drill dias.
587 * Maybe also put error into stats? */
588 size
= image
->aperture
[tool_num
]->parameter
[0];
589 drill_stats_modify_drill_list(stats
->drill_list
,
593 /* Now go back and update all tool dias, since
594 * tools are displayed in inch units
596 image
->aperture
[tool_num
]->parameter
[0] /= 25.4;
601 state
->number_format
= state
->backup_number_format
;
602 state
->unit
= GERBV_UNIT_MM
;
605 case DRILL_M_IMPERIAL
:
607 if (state
->number_format
!= FMT_00_0000
)
608 /* save metric format definition for later */
609 state
->backup_number_format
= state
->number_format
;
610 state
->number_format
= FMT_00_0000
;
612 state
->unit
= GERBV_UNIT_INCH
;
616 case DRILL_M_LONGMESSAGE
:
617 case DRILL_M_MESSAGE
:
618 case DRILL_M_CANNEDTEXT
:
620 string
= g_strdup_printf("Message embedded in drill file: '%s'\n",
622 drill_stats_add_error(stats
->error_list
,
628 case DRILL_M_NOT_IMPLEMENTED
:
629 case DRILL_M_ENDPATTERN
:
630 case DRILL_M_TIPCHECK
:
633 /* M00 has optional arguments */
635 case DRILL_M_ENDREWIND
:
636 goto drill_parse_end
;
638 case DRILL_M_METRICHEADER
:
639 state
->unit
= GERBV_UNIT_MM
;
642 drill_stats_add_error(stats
->error_list
,
644 "Undefined M code found.\n",
645 GERBV_MESSAGE_ERROR
);
650 if(state
->curr_section
== DRILL_HEADER
) {
651 drill_stats_add_error(stats
->error_list
,
653 "R codes are not allowed in the header.\n",
654 GERBV_MESSAGE_ERROR
);
656 double start_x
, start_y
;
657 double step_x
, step_y
;
661 * This is the "Repeat hole" command. Format is:
663 * This repeats the previous hole stepping in the X and
664 * Y increments give. Example:
665 * R04X0.1 -- repeats the drill hole 4 times, stepping
666 * 0.1" in the X direction. Note that the X and Y step
667 * sizes default to zero if not given and that they use
668 * the same format and units as the normal X,Y
673 start_x
= state
->curr_x
;
674 start_y
= state
->curr_y
;
678 /* figure out how many repeats there are */
681 while ( '0' <= c
&& c
<= '9') {
682 rcnt
= 10*rcnt
+ (c
- '0');
685 dprintf ("working on R code (repeat) with a number of reps equal to %d\n", rcnt
);
689 step_x
= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
695 step_y
= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
700 dprintf ("Getting ready to repeat the drill %d times with delta_x = %g, delta_y = %g\n", rcnt
, step_x
, step_y
);
702 /* spit out the drills */
703 for (c
= 1 ; c
<= rcnt
; c
++) {
704 state
->curr_x
= start_x
+ c
*step_x
;
705 state
->curr_y
= start_y
+ c
*step_y
;
706 dprintf (" Repeat #%d -- new location is (%g, %g)\n", c
, state
->curr_x
, state
->curr_y
);
707 curr_net
= drill_add_drill_hole (image
, state
, stats
, curr_net
);
713 drill_stats_add_error(stats
->error_list
,
715 "Drill file sets spindle speed -- ignoring.\n",
720 drill_parse_T_code(fd
, state
, image
);
724 tmps
= get_line (fd
);
725 /* Silently ignore VER,1. Not sure what others are allowed */
726 if (strcmp (tmps
, "VER,1") != 0) {
727 string
= g_strdup_printf("Undefined header line = '%s'\n",tmps
);
728 drill_stats_add_error(stats
->error_list
,
730 g_strdup_printf("Undefined header line = '%s'\n",tmps
),
739 /* Hole coordinate found. Do some parsing */
740 drill_parse_coordinate(fd
, read
, image
, state
);
742 /* add the new drill hole */
743 curr_net
= drill_add_drill_hole (image
, state
, stats
, curr_net
);
747 state
->curr_section
= DRILL_DATA
;
749 case 10 : /* White space */
755 if(state
->curr_section
== DRILL_HEADER
) {
756 /* Unrecognised crap in the header is thrown away */
757 drill_stats_add_error(stats
->error_list
,
759 "Undefined codes found in header.\n",
760 GERBV_MESSAGE_ERROR
);
763 string
= g_strdup_printf("Undefined header line = '%s'\n",
765 drill_stats_add_error(stats
->error_list
,
772 string
= g_strdup_printf("Undefined character '%c' [0x%02x] found inside data, ignoring\n",
774 drill_stats_add_error(stats
->error_list
,
777 GERBV_MESSAGE_ERROR
);
782 drill_stats_add_error(stats
->error_list
,
784 "No EOF found in drill file.\n",
785 GERBV_MESSAGE_ERROR
);
788 dprintf ("%s(): Populating file attributes\n", __FUNCTION__
);
790 switch (state
->unit
) {
792 image
->info
->attr_list
[HA_xy_units
].default_val
.int_value
= UNITS_MM
;
796 image
->info
->attr_list
[HA_xy_units
].default_val
.int_value
= UNITS_INCH
;
800 switch (state
->number_format
) {
803 image
->info
->attr_list
[HA_digits
].default_val
.int_value
= 2;
807 image
->info
->attr_list
[HA_digits
].default_val
.int_value
= 3;
811 image
->info
->attr_list
[HA_digits
].default_val
.int_value
= 4;
815 dprintf ("%s(): Keeping user specified number of decimal places (%d)\n",
817 image
->info
->attr_list
[HA_digits
].default_val
.int_value
);
824 switch (image
->format
->omit_zeros
) {
825 case GERBV_OMIT_ZEROS_LEADING
:
826 image
->info
->attr_list
[HA_supression
].default_val
.int_value
= SUP_LEAD
;
829 case GERBV_OMIT_ZEROS_TRAILING
:
830 image
->info
->attr_list
[HA_supression
].default_val
.int_value
= SUP_TRAIL
;
834 image
->info
->attr_list
[HA_supression
].default_val
.int_value
= SUP_NONE
;
841 } /* parse_drillfile */
844 /* -------------------------------------------------------------- */
846 * Checks for signs that this is a drill file
847 * Returns TRUE if it is, FALSE if not.
850 drill_file_p(gerb_file_t
*fd
, gboolean
*returnFoundBinary
)
852 char *buf
= NULL
, *tbuf
= NULL
;
856 int zero
= 48; /* ascii 0 */
857 int nine
= 57; /* ascii 9 */
859 gboolean found_binary
= FALSE
;
860 gboolean found_M48
= FALSE
;
861 gboolean found_M30
= FALSE
;
862 gboolean found_percent
= FALSE
;
863 gboolean found_T
= FALSE
;
864 gboolean found_X
= FALSE
;
865 gboolean found_Y
= FALSE
;
866 gboolean end_comments
= FALSE
;
868 dprintf ("drill_file_p(%p, %p), fd->fd = %p, fd->zfd = %p %s\n",
869 fd
, returnFoundBinary
, fd
->fd
, fd
->zfd
, fd
->filename
);
873 /* Release the buffer if it was in use previously */
879 /* Read one line at the time using gerb_file operations to support zip*/
880 tbuf
= gerb_fgetstring(fd
, '\n');
887 len
= ((fd
->datalen
- idx
) < MAXL
)? (fd
->datalen
- idx
) : MAXL
;
888 memset(buf
, 0, MAXL
);
889 memcpy(buf
, &fd
->data
[idx
], len
);
891 dprintf ("buf = \"%s\"\n", buf
);
903 dprintf ("buf = \"%s\"\n", buf
);
905 /* Check for comments at top of file. */
906 if ((!end_comments
) &&
907 (g_strstr_len(buf
, len
, ";") != NULL
)) {
908 for (i
= 0; i
< len
- 1 ; ++i
){
909 if ((buf
[i
] == '\n') &&
911 (buf
[i
+1] != '\r') &&
912 (buf
[i
+1] != '\n')) {
914 buf
= &tbuf
[i
+1];/* set rest of parser to end of comments */
924 /* First look through the file for indications of its type */
926 /* check that file is not binary (non-printing chars) */
927 for (i
= 0; i
< len
; i
++) {
928 ascii
= (int) buf
[i
];
929 if ((ascii
> 128) || (ascii
< 0)) {
934 /* Check for M48 = start of drill header */
935 if (g_strstr_len(buf
, len
, "M48")) {
939 /* Check for M30 = end of drill program */
940 if (g_strstr_len(buf
, len
, "M30")) {
942 found_M30
= TRUE
; /* Found M30 after % = good */
946 /* Check for % on its own line at end of header */
947 if ((letter
= g_strstr_len(buf
, len
, "%")) != NULL
) {
948 if ((letter
[1] == '\r') || (letter
[1] == '\n'))
949 found_percent
= TRUE
;
952 /* Check for T<number> */
953 if ((letter
= g_strstr_len(buf
, len
, "T")) != NULL
) {
954 if (!found_T
&& (found_X
|| found_Y
)) {
955 found_T
= FALSE
; /* Found first T after X or Y */
957 if (isdigit( (int) letter
[1])) { /* verify next char is digit */
963 /* look for X<number> or Y<number> */
964 if ((letter
= g_strstr_len(buf
, len
, "X")) != NULL
) {
965 ascii
= (int) letter
[1]; /* grab char after X */
966 if ((ascii
>= zero
) && (ascii
<= nine
)) {
970 if ((letter
= g_strstr_len(buf
, len
, "Y")) != NULL
) {
971 ascii
= (int) letter
[1]; /* grab char after Y */
972 if ((ascii
>= zero
) && (ascii
<= nine
)) {
980 *returnFoundBinary
= found_binary
;
984 /* Now form logical expression determining if this is a drill file */
985 if ( ((found_X
|| found_Y
) && found_T
) &&
986 (found_M48
|| (found_percent
&& found_M30
)) ) {
988 } else if (found_M48
&& found_T
&& found_percent
&& found_M30
) {
989 /* Pathological case of drill file with valid header
990 and EOF but no drill XY locations. */
998 /* -------------------------------------------------------------- */
999 /* Parse tool definition. This can get a bit tricky since it can
1000 appear in the header and/or data section.
1001 Returns tool number on success, -1 on error */
1003 drill_parse_T_code(gerb_file_t
*fd
, drill_state_t
*state
, gerbv_image_t
*image
)
1006 gboolean done
= FALSE
;
1009 gerbv_drill_stats_t
*stats
= image
->drill_stats
;
1013 /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
1014 broken headers from Orcad, which is crap */
1015 temp
= gerb_fgetc(fd
);
1016 dprintf("Found a char %d after the T\n", temp
);
1018 /* might be a tool tool change stop switch on/off*/
1019 if((temp
== 'C') && ((fd
->ptr
+ 2) < fd
->datalen
)){
1020 if(gerb_fgetc(fd
) == 'S'){
1021 if (gerb_fgetc(fd
) == 'T' ){
1023 tmps
= get_line(fd
++);
1024 string
= g_strdup_printf("Tool change stop switch found: %s\n", tmps
);
1025 drill_stats_add_error(stats
->error_list
,
1028 GERBV_MESSAGE_NOTE
);
1038 if( !(isdigit(temp
) != 0 || temp
== '+' || temp
=='-') ) {
1040 drill_stats_add_error(stats
->error_list
,
1042 "Orcad bug: Junk text found in place of tool definition.\n",
1043 GERBV_MESSAGE_ERROR
);
1044 tmps
= get_line(fd
);
1045 string
= g_strdup_printf("Junk text = %s\n",
1047 drill_stats_add_error(stats
->error_list
,
1050 GERBV_MESSAGE_NOTE
);
1053 drill_stats_add_error(stats
->error_list
,
1055 "Ignorning junk text.\n",
1056 GERBV_MESSAGE_WARNING
);
1062 tool_num
= (int) gerb_fgetint(fd
, NULL
);
1063 dprintf ("In %s: handling tool_num = %d\n", __FUNCTION__
, tool_num
);
1066 return tool_num
; /* T00 is a command to unload the drill */
1068 if ( (tool_num
< TOOL_MIN
) || (tool_num
>= TOOL_MAX
) ) {
1069 string
= g_strdup_printf("Drill number out of bounds: %d.\n", tool_num
);
1070 drill_stats_add_error(stats
->error_list
,
1073 GERBV_MESSAGE_ERROR
);
1077 /* Set the current tool to the correct one */
1078 state
->current_tool
= tool_num
;
1080 /* Check for a size definition */
1081 temp
= gerb_fgetc(fd
);
1083 /* This bit of code looks for a tool definition by scanning for strings
1084 * of form TxxC, TxxF, TxxS. */
1087 switch((char)temp
) {
1089 size
= read_double(fd
, state
->header_number_format
, GERBV_OMIT_ZEROS_TRAILING
, state
->decimals
);
1090 dprintf ("%s: Read a size of %g %s\n", __FUNCTION__
, size
,
1091 state
->unit
== GERBV_UNIT_MM
? "mm" : "inch");
1092 if(state
->unit
== GERBV_UNIT_MM
) {
1094 } else if(size
>= 4.0) {
1095 /* If the drill size is >= 4 inches, assume that this
1096 must be wrong and that the units are mils.
1097 The limit being 4 inches is because the smallest drill
1098 I've ever seen used is 0,3mm(about 12mil). Half of that
1099 seemed a bit too small a margin, so a third it is */
1100 string
= g_strdup_printf("Read a drill of diameter %g inches.\n", size
);
1101 drill_stats_add_error(stats
->error_list
,
1104 GERBV_MESSAGE_ERROR
);
1106 string
= g_strdup_printf("Assuming units are mils.\n");
1107 drill_stats_add_error(stats
->error_list
,
1110 GERBV_MESSAGE_WARNING
);
1115 if(size
<= 0. || size
>= 10000.) {
1116 string
= g_strdup_printf("Unreasonable drill size found for drill %d: %g\n", tool_num
, size
);
1117 drill_stats_add_error(stats
->error_list
,
1120 GERBV_MESSAGE_ERROR
);
1123 if(image
->aperture
[tool_num
] != NULL
) {
1124 /* allow a redefine of a tool only if the new definition is exactly the same.
1125 * This avoid lots of spurious complaints with the output of some cad
1126 * tools while keeping complaints if there is a true problem
1128 if (image
->aperture
[tool_num
]->parameter
[0] != size
||
1129 image
->aperture
[tool_num
]->type
!= GERBV_APTYPE_CIRCLE
||
1130 image
->aperture
[tool_num
]->nuf_parameters
!= 1 ||
1131 image
->aperture
[tool_num
]->unit
!= GERBV_UNIT_INCH
) {
1132 string
= g_strdup_printf("Found redefinition of drill %d.\n", tool_num
);
1133 drill_stats_add_error(stats
->error_list
,
1136 GERBV_MESSAGE_ERROR
);
1140 image
->aperture
[tool_num
] =
1141 (gerbv_aperture_t
*)g_malloc(sizeof(gerbv_aperture_t
));
1142 if (image
->aperture
[tool_num
] == NULL
) {
1143 GERB_FATAL_ERROR("malloc tool failed\n");
1145 /* make sure we zero out all aperature parameters */
1146 memset((void *)image
->aperture
[tool_num
], 0, sizeof(gerbv_aperture_t
));
1147 /* There's really no way of knowing what unit the tools
1148 are defined in without sneaking a peek in the rest of
1149 the file first. That's done in drill_guess_format() */
1150 image
->aperture
[tool_num
]->parameter
[0] = size
;
1151 image
->aperture
[tool_num
]->type
= GERBV_APTYPE_CIRCLE
;
1152 image
->aperture
[tool_num
]->nuf_parameters
= 1;
1153 image
->aperture
[tool_num
]->unit
= GERBV_UNIT_INCH
;
1157 /* Add the tool whose definition we just found into the list
1158 * of tools for this layer used to generate statistics. */
1159 stats
= image
->drill_stats
;
1160 string
= g_strdup_printf("%s", (state
->unit
== GERBV_UNIT_MM
? "mm" : "inch"));
1161 drill_stats_add_to_drill_list(stats
->drill_list
,
1163 state
->unit
== GERBV_UNIT_MM
? size
*25.4 : size
,
1170 /* Silently ignored. They're not important. */
1171 gerb_fgetint(fd
, NULL
);
1175 /* Stop when finding anything but what's expected
1176 (and put it back) */
1180 } /* switch((char)temp) */
1182 if( (temp
= gerb_fgetc(fd
)) == EOF
) {
1183 drill_stats_add_error(stats
->error_list
,
1185 "Unexpected EOF encountered header of drill file.\n",
1186 GERBV_MESSAGE_ERROR
);
1188 } /* while(!done) */ /* Done looking at tool definitions */
1190 /* Catch the tools that aren't defined.
1191 This isn't strictly a good thing, but at least something is shown */
1192 if(image
->aperture
[tool_num
] == NULL
) {
1195 image
->aperture
[tool_num
] =
1196 (gerbv_aperture_t
*)g_malloc(sizeof(gerbv_aperture_t
));
1197 if (image
->aperture
[tool_num
] == NULL
) {
1198 GERB_FATAL_ERROR("malloc tool failed\n");
1200 /* make sure we zero out all aperature parameters */
1201 memset((void *)image
->aperture
[tool_num
], 0, sizeof(gerbv_aperture_t
));
1203 /* See if we have the tool table */
1204 dia
= gerbv_get_tool_diameter(tool_num
);
1207 * There is no tool. So go out and make some.
1208 * This size calculation is, of course, totally bogus.
1210 dia
= (double)(16 + 8 * tool_num
) / 1000;
1212 * Oooh, this is sooo ugly. But some CAD systems seem to always
1213 * use T00 at the end of the file while others that don't have
1214 * tool definitions inside the file never seem to use T00 at all.
1217 string
= g_strdup_printf("Tool %02d used without being defined\n", tool_num
);
1218 drill_stats_add_error(stats
->error_list
,
1221 GERBV_MESSAGE_ERROR
);
1223 string
= g_strdup_printf("Setting a default size of %g\"\n", dia
);
1224 drill_stats_add_error(stats
->error_list
,
1227 GERBV_MESSAGE_WARNING
);
1232 image
->aperture
[tool_num
]->type
= GERBV_APTYPE_CIRCLE
;
1233 image
->aperture
[tool_num
]->nuf_parameters
= 1;
1234 image
->aperture
[tool_num
]->parameter
[0] = dia
;
1236 /* Add the tool whose definition we just found into the list
1237 * of tools for this layer used to generate statistics. */
1238 if (tool_num
!= 0) { /* Only add non-zero tool nums.
1239 * Zero = unload command. */
1240 stats
= image
->drill_stats
;
1241 string
= g_strdup_printf("%s",
1242 (state
->unit
== GERBV_UNIT_MM
? "mm" : "inch"));
1243 drill_stats_add_to_drill_list(stats
->drill_list
,
1245 state
->unit
== GERBV_UNIT_MM
? dia
*25.4 : dia
,
1249 } /* if(image->aperture[tool_num] == NULL) */
1252 } /* drill_parse_T_code */
1255 /* -------------------------------------------------------------- */
1257 drill_parse_M_code(gerb_file_t
*fd
, drill_state_t
*state
, gerbv_image_t
*image
)
1261 gerbv_drill_stats_t
*stats
= image
->drill_stats
;
1264 dprintf("---> entering drill_parse_M_code ...\n");
1266 read
[0] = gerb_fgetc(fd
);
1267 read
[1] = gerb_fgetc(fd
);
1269 if ((read
[0] == EOF
) || (read
[1] == EOF
))
1270 drill_stats_add_error(stats
->error_list
,
1272 "Unexpected EOF found while parsing M code.\n",
1273 GERBV_MESSAGE_ERROR
);
1274 op
[0] = read
[0], op
[1] = read
[1], op
[2] = 0;
1276 if (strncmp(op
, "00", 2) == 0) {
1278 result
= DRILL_M_END
;
1279 } else if (strncmp(op
, "01", 2) == 0) {
1281 result
= DRILL_M_ENDPATTERN
;
1282 } else if (strncmp(op
, "18", 2) == 0) {
1284 result
= DRILL_M_TIPCHECK
;
1285 } else if (strncmp(op
, "25", 2) == 0) {
1287 result
= DRILL_M_BEGINPATTERN
;
1288 } else if (strncmp(op
, "31", 2) == 0) {
1290 result
= DRILL_M_BEGINPATTERN
;
1291 } else if (strncmp(op
, "30", 2) == 0) {
1293 result
= DRILL_M_ENDREWIND
;
1294 } else if (strncmp(op
, "45", 2) == 0) {
1296 result
= DRILL_M_LONGMESSAGE
;
1297 } else if (strncmp(op
, "47", 2) == 0) {
1299 result
= DRILL_M_MESSAGE
;
1300 } else if (strncmp(op
, "48", 2) == 0) {
1302 result
= DRILL_M_HEADER
;
1303 } else if (strncmp(op
, "71", 2) == 0) {
1306 result
= DRILL_M_METRIC
;
1307 } else if (strncmp(op
, "72", 2) == 0) {
1310 result
= DRILL_M_IMPERIAL
;
1311 } else if (strncmp(op
, "95", 2) == 0) {
1313 result
= DRILL_M_ENDHEADER
;
1314 } else if (strncmp(op
, "97", 2) == 0) {
1316 result
= DRILL_M_CANNEDTEXT
;
1317 } else if (strncmp(op
, "98", 2) == 0) {
1319 return DRILL_M_CANNEDTEXT
;
1320 } else if (state
->curr_section
== DRILL_HEADER
&&
1321 strncmp(op
, "ET", 2) == 0) {
1322 /* METRIC is not an actual M code but a command that is only
1323 acceptable within the header.
1326 METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
1328 if ('R' == gerb_fgetc(fd
) &&
1329 'I' == gerb_fgetc(fd
) &&
1330 'C' == gerb_fgetc(fd
)) {
1332 if (',' == gerb_fgetc(fd
)) {
1335 /* Is it tzlz, or zerofmt? */
1336 switch ((c
= gerb_fgetc(fd
))) {
1339 if ('Z' != gerb_fgetc(fd
))
1343 dprintf ("%s(): Detected a file that probably has trailing zero supression\n", __FUNCTION__
);
1346 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_TRAILING
;
1351 dprintf ("%s(): Detected a file that probably has leading zero supression\n", __FUNCTION__
);
1354 image
->format
->omit_zeros
= GERBV_OMIT_ZEROS_LEADING
;
1359 /* Default metric number format is 6-digit, 1 um
1360 resolution. The header number format (for T#C#
1361 definitions) is fixed to that, while the number
1362 format within the file can differ. */
1363 state
->header_number_format
=
1364 state
->number_format
= FMT_000_000
;
1365 state
->decimals
= 3;
1370 /* anticipate number format will follow */
1375 if ('0' != gerb_fgetc(fd
) ||
1376 '0' != gerb_fgetc(fd
))
1378 /* We just parsed three 0s, the remainder options
1379 so far are: .000 | .00 | 0.00 */
1380 read
[0] = gerb_fgetc(fd
);
1381 read
[1] = gerb_fgetc(fd
);
1382 if (read
[0] == EOF
|| read
[1] == EOF
)
1386 if (strcmp(op
, "0.") == 0) {
1387 /* expecting FMT_0000_00,
1388 two trailing 0s must follow */
1389 if ('0' != gerb_fgetc(fd
) ||
1390 '0' != gerb_fgetc(fd
))
1395 state
->number_format
= FMT_0000_00
;
1396 state
->decimals
= 2;
1400 if (strcmp(op
, ".0") != 0)
1402 /* must be either FMT_000_000 or FMT_000_00, depending
1403 on whether one or two 0s are following */
1404 if ('0' != gerb_fgetc(fd
))
1406 if ('0' == gerb_fgetc(fd
) && state
->autod
)
1408 state
->number_format
= FMT_000_000
;
1409 state
->decimals
= 3;
1415 state
->number_format
= FMT_000_00
;
1416 state
->decimals
= 2;
1424 drill_stats_add_error(stats
->error_list
,
1426 "Found junk after METRIC command\n",
1427 GERBV_MESSAGE_WARNING
);
1437 return DRILL_M_METRICHEADER
;
1441 result
= DRILL_M_UNKNOWN
;
1444 dprintf("<---- ...leaving drill_parse_M_code.\n");
1446 } /* drill_parse_M_code */
1449 /* -------------------------------------------------------------- */
1451 drill_parse_G_code(gerb_file_t
*fd
, gerbv_image_t
*image
)
1455 gerbv_drill_stats_t
*stats
= image
->drill_stats
;
1458 dprintf("---> entering drill_parse_G_code ...\n");
1460 read
[0] = gerb_fgetc(fd
);
1461 read
[1] = gerb_fgetc(fd
);
1463 if ((read
[0] == EOF
) || (read
[1] == EOF
)) {
1464 drill_stats_add_error(stats
->error_list
,
1466 "Unexpected EOF found while parsing G code.\n",
1467 GERBV_MESSAGE_ERROR
);
1470 op
[0] = read
[0], op
[1] = read
[1], op
[2] = 0;
1472 if (strncmp(op
, "00", 2) == 0) {
1474 result
= DRILL_G_ROUT
;
1475 } else if (strncmp(op
, "01", 2) == 0) {
1477 result
= DRILL_G_LINEARMOVE
;
1478 } else if (strncmp(op
, "02", 2) == 0) {
1480 result
= DRILL_G_CWMOVE
;
1481 } else if (strncmp(op
, "03", 2) == 0) {
1483 result
= DRILL_G_CCWMOVE
;
1484 } else if (strncmp(op
, "05", 2) == 0) {
1486 result
= DRILL_G_DRILL
;
1487 } else if (strncmp(op
, "90", 2) == 0) {
1489 result
= DRILL_G_ABSOLUTE
;
1490 } else if (strncmp(op
, "91", 2) == 0) {
1492 result
= DRILL_G_INCREMENTAL
;
1493 } else if (strncmp(op
, "93", 2) == 0) {
1495 result
= DRILL_G_ZEROSET
;
1498 result
= DRILL_G_UNKNOWN
;
1501 dprintf("<---- ...leaving drill_parse_G_code.\n");
1504 } /* drill_parse_G_code */
1507 /* -------------------------------------------------------------- */
1508 /* Parse on drill file coordinate.
1509 Returns nothing, but modifies state */
1511 drill_parse_coordinate(gerb_file_t
*fd
, char firstchar
,
1512 gerbv_image_t
*image
, drill_state_t
*state
)
1517 if(state
->coordinate_mode
== DRILL_MODE_ABSOLUTE
) {
1518 if(firstchar
== 'X') {
1519 state
->curr_x
= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
1520 if((read
= (char)gerb_fgetc(fd
)) == 'Y') {
1521 state
->curr_y
= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
1524 state
->curr_y
= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
1526 } else if(state
->coordinate_mode
== DRILL_MODE_INCREMENTAL
) {
1527 if(firstchar
== 'X') {
1528 state
->curr_x
+= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
1529 if((read
= (char)gerb_fgetc(fd
)) == 'Y') {
1530 state
->curr_y
+= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
1533 state
->curr_y
+= read_double(fd
, state
->number_format
, image
->format
->omit_zeros
, state
->decimals
);
1537 } /* drill_parse_coordinate */
1540 /* Allocates and returns a new drill_state structure
1541 Returns state pointer on success, NULL on ERROR */
1542 static drill_state_t
*
1543 new_state(drill_state_t
*state
)
1545 state
= (drill_state_t
*)g_malloc(sizeof(drill_state_t
));
1546 if (state
!= NULL
) {
1547 /* Init structure */
1548 memset((void *)state
, 0, sizeof(drill_state_t
));
1549 state
->curr_section
= DRILL_NONE
;
1550 state
->coordinate_mode
= DRILL_MODE_ABSOLUTE
;
1551 state
->origin_x
= 0.0;
1552 state
->origin_y
= 0.0;
1553 state
->unit
= GERBV_UNIT_UNSPECIFIED
;
1554 state
->backup_number_format
= FMT_000_000
; /* only used for METRIC */
1555 state
->header_number_format
= state
->number_format
= FMT_00_0000
; /* i. e. INCH */
1557 state
->decimals
= 4;
1564 /* -------------------------------------------------------------- */
1565 /* Reads one double from fd and returns it.
1566 If a decimal point is found, fmt is not used. */
1568 read_double(gerb_file_t
*fd
, enum number_fmt_t fmt
, gerbv_omit_zeros_t omit_zeros
, int decimals
)
1571 char temp
[DRILL_READ_DOUBLE_SIZE
];
1572 int i
= 0, ndigits
= 0;
1574 gboolean decimal_point
= FALSE
;
1575 gboolean sign_prepend
= FALSE
;
1577 dprintf("%s(%p, %d, %d, %d)\n", __FUNCTION__
, fd
, fmt
, omit_zeros
, decimals
);
1579 memset(temp
, 0, sizeof(temp
));
1581 read
= gerb_fgetc(fd
);
1582 while(read
!= EOF
&& i
< (DRILL_READ_DOUBLE_SIZE
-1) &&
1583 (isdigit(read
) || read
== '.' || read
== ',' || read
== '+' || read
== '-')) {
1584 if(read
== ',' || read
== '.') decimal_point
= TRUE
;
1587 * FIXME -- if we are going to do this, don't we need a
1588 * locale-independent strtod()? I think pcb has one.
1591 read
= '.'; /* adjust for strtod() */
1593 if(isdigit(read
)) ndigits
++;
1595 if(read
== '-' || read
== '+')
1596 sign_prepend
= TRUE
;
1598 temp
[i
++] = (char)read
;
1599 read
= gerb_fgetc(fd
);
1604 if (decimal_point
) {
1605 result
= strtod(temp
, NULL
);
1609 char tmp2
[DRILL_READ_DOUBLE_SIZE
];
1611 memset(tmp2
, 0, sizeof(tmp2
));
1613 dprintf("%s(): omit_zeros = %d, fmt = %d\n", __FUNCTION__
, omit_zeros
, fmt
);
1614 /* Nothing to take care for when leading zeros are
1616 if (omit_zeros
== GERBV_OMIT_ZEROS_TRAILING
) {
1635 wantdigits
= decimals
;
1639 /* cannot happen, just plugs a compiler warning */
1640 fprintf(stderr
, "%s(): omit_zeros == GERBV_OMIT_ZEROS_TRAILING but fmt = %d.\n"
1641 "This should never have happened\n", __FUNCTION__
, fmt
);
1645 /* need to add an extra char for '+' or '-' */
1651 * we need at least wantdigits + one for the decimal place
1652 * + one for the terminating null character
1654 if (wantdigits
> sizeof(tmp2
) - 2) {
1655 fprintf(stderr
, "%s(): wantdigits = %d which exceeds the maximum allowed size\n",
1656 __FUNCTION__
, wantdigits
);
1661 * After we have read the correct number of digits
1662 * preceeding the decimal point, insert a decimal point
1663 * and append the rest of the digits.
1665 dprintf("%s(): wantdigits = %d, strlen(\"%s\") = %ld\n",
1666 __FUNCTION__
, wantdigits
, temp
, (long) strlen(temp
));
1667 for (i
= 0 ; i
< wantdigits
; i
++) {
1670 tmp2
[wantdigits
] = '.';
1671 for (i
= wantdigits
; i
<= strlen(temp
) ; i
++) {
1672 tmp2
[i
+1] = temp
[i
];
1674 dprintf("%s(): After dealing with trailing zero supression, convert \"%s\"\n", __FUNCTION__
, tmp2
);
1677 for (i
= 0 ; i
<= strlen(tmp2
) && i
< sizeof (temp
) ; i
++) {
1684 * figure out the scale factor when we are not suppressing
1702 scale
= pow (10.0, -1.0*decimals
);
1706 /* cannot happen, just plugs a compiler warning */
1707 fprintf (stderr
, "%s(): Unhandled fmt ` %d\n", __FUNCTION__
, fmt
);
1712 result
= strtod(temp
, NULL
) * scale
;
1719 /* -------------------------------------------------------------- */
1720 /* Eats all characters up to and including
1721 the first one of CR or LF */
1723 eat_line(gerb_file_t
*fd
)
1725 int read
= gerb_fgetc(fd
);
1727 while(read
!= 10 && read
!= 13) {
1728 if (read
== EOF
) return;
1729 read
= gerb_fgetc(fd
);
1733 /* -------------------------------------------------------------- */
1735 get_line(gerb_file_t
*fd
)
1737 int read
= gerb_fgetc(fd
);
1739 gchar
*tmps
=g_strdup("");
1741 while(read
!= 10 && read
!= 13) {
1744 retstring
= g_strdup_printf("%s%c", tmps
, read
);
1746 /* since g_strdup_printf allocates memory, we need to free it */
1752 read
= gerb_fgetc(fd
);