drill.c

Go to the documentation of this file.
00001 /*
00002  * gEDA - GNU Electronic Design Automation
00003  * drill.c
00004  * Copyright (C) 2000-2006 Andreas Andersson
00005  *
00006  * $Id$
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00021  */
00022 
00028 /*
00029  * 21 Feb 2007 patch for metric drill files:
00030  * 1) METRIC/INCH commands (partly) parsed to define units of the header
00031  * 2) units of the header and the program body are independent
00032  * 3) ICI command parsed in the header
00033  */
00034 
00035 #ifdef HAVE_CONFIG_H
00036 #include <config.h>
00037 #endif
00038 
00039 #include <stdlib.h>
00040 #include <glib.h>
00041 #include <locale.h>
00042 
00043 #ifdef HAVE_STRING_H
00044 #include <string.h>
00045 #endif
00046 
00047 #include <math.h>  /* pow() */
00048 #include <ctype.h>
00049 
00050 #include <sys/types.h>
00051 #include <sys/stat.h>
00052 
00053 #ifdef HAVE_UNISTD_H
00054 #include <unistd.h>
00055 #endif
00056 
00057 #include "gerbv.h"
00058 #include "drill.h"
00059 #include "drill_stats.h"
00060 
00061 #include "common.h"
00062 
00063 /* DEBUG printing.  #define DEBUG 1 in config.h to use this fcn. */
00064 #define dprintf if(DEBUG) printf
00065 
00066 #define NOT_IMPL(fd, s) do { \
00067                              GERB_MESSAGE("Not Implemented:%s\n", s); \
00068                            } while(0)
00069 
00070 #define MAXL 200
00071 
00072 
00073 #undef max
00074 #define max(a,b) ((a) > (b) ? (a) : (b))
00075 #undef min
00076 #define min(a,b) ((a) < (b) ? (a) : (b))
00077 
00078 enum drill_file_section_t {DRILL_NONE, DRILL_HEADER, DRILL_DATA};
00079 enum drill_coordinate_mode_t {DRILL_MODE_ABSOLUTE, DRILL_MODE_INCREMENTAL};
00080 
00081 enum drill_m_code_t {DRILL_M_UNKNOWN, DRILL_M_NOT_IMPLEMENTED,
00082                    DRILL_M_END, DRILL_M_ENDREWIND,
00083                    DRILL_M_MESSAGE, DRILL_M_LONGMESSAGE,
00084                    DRILL_M_HEADER, DRILL_M_ENDHEADER,
00085                    DRILL_M_METRIC, DRILL_M_IMPERIAL,
00086                    DRILL_M_BEGINPATTERN, DRILL_M_ENDPATTERN,
00087                    DRILL_M_CANNEDTEXT, DRILL_M_TIPCHECK,
00088                    DRILL_M_METRICHEADER, DRILL_M_IMPERIALHEADER};
00089 
00090 
00091 enum drill_g_code_t {DRILL_G_ABSOLUTE, DRILL_G_INCREMENTAL,
00092                    DRILL_G_ZEROSET, DRILL_G_UNKNOWN,
00093                    DRILL_G_ROUT, DRILL_G_DRILL,
00094                    DRILL_G_LINEARMOVE, DRILL_G_CWMOVE, DRILL_G_CCWMOVE};
00095 
00096 enum number_fmt_t {FMT_00_0000 /* INCH */,
00097                  FMT_000_000 /* METRIC 6-digit, 1 um */,
00098                  FMT_000_00  /* METRIC 5-digit, 10 um */,
00099                  FMT_0000_00 /* METRIC 6-digit, 10 um */,
00100                  FMT_USER    /* User defined format */};
00101 
00102 typedef struct drill_state {
00103     double curr_x;
00104     double curr_y;
00105     int current_tool;
00106     int curr_section;
00107     int coordinate_mode;
00108     double origin_x;
00109     double origin_y;
00110     gerbv_unit_t unit;
00111     /* number_format is used throughout the file itself.
00112 
00113        header_number_format is used to parse the tool definition C
00114        codes within the header.  It is fixed to FMT_00_0000 for INCH
00115        measures, and FMT_000_000 (1 um resolution) for metric
00116        measures. */
00117     enum number_fmt_t number_format, header_number_format;
00118     /* Used as a backup when temporarily switching to INCH. */
00119     enum number_fmt_t backup_number_format;
00120 
00121     /* 0 means we don't try to autodetect any of the other values */
00122     int autod;
00123     
00124     /* in FMT_USER this specifies the number of digits after the decimal 
00125      * place in the file
00126      */
00127     int decimals;
00128 
00129 } drill_state_t;
00130 
00131 /* Local function prototypes */
00132 static int drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image);
00133 static int drill_parse_M_code(gerb_file_t *fd, drill_state_t *state, 
00134                            gerbv_image_t *image);
00135 static int drill_parse_T_code(gerb_file_t *fd, drill_state_t *state, 
00136                            gerbv_image_t *image);
00137 static void drill_parse_coordinate(gerb_file_t *fd, char firstchar, 
00138                                gerbv_image_t *image, drill_state_t *state);
00139 static drill_state_t *new_state(drill_state_t *state);
00140 static double read_double(gerb_file_t *fd, enum number_fmt_t fmt, 
00141                        gerbv_omit_zeros_t omit_zeros, int decimals);
00142 static void eat_line(gerb_file_t *fd);
00143 static char *get_line(gerb_file_t *fd);
00144 
00145 /* -------------------------------------------------------------- */
00146 /* This is the list of specific attributes a drill file may have from
00147  * the point of view of parsing it.
00148  */
00149 
00150 static const char *supression_list[] = {
00151     "None",
00152 #define SUP_NONE 0
00153     "Leading",
00154 #define SUP_LEAD 1
00155     "Trailing",
00156 #define SUP_TRAIL 2
00157     0
00158 };
00159 
00160 static const char *units_list[] = {
00161     "inch",
00162 #define UNITS_INCH 0
00163     "mil (1/1000 inch)",
00164 #define UNITS_MIL 1
00165     "mm",
00166 #define UNITS_MM 2
00167     0
00168 };
00169 
00170 static gerbv_HID_Attribute drill_attribute_list[] = {
00171     /* This should be first */
00172   {"Autodetect file format", "Try to autodetect the file format",
00173    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
00174 #define HA_auto 0
00175 
00176   {"zero_supression", "Zero supression",
00177    HID_Enum, 0, 0, {0, 0, 0}, supression_list, 0},
00178 #define HA_supression 1
00179 
00180   {"units", "Units",
00181    HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
00182 #define HA_xy_units 2
00183 
00184 #if 0
00185   {"tool_units", "Tool size units",
00186    HID_Enum, 0, 0, {0, 0, 0}, units_list, 0},
00187 #define HA_tool_units 3
00188 #endif
00189 
00190   {"digits", "Number of digits",
00191    HID_Integer, 0, 20, {5, 0, 0}, 0, 0},
00192 #define HA_digits 3
00193 };
00194 
00195 
00196 void
00197 drill_attribute_merge (gerbv_HID_Attribute *dest, int ndest, gerbv_HID_Attribute *src, int nsrc)
00198 {
00199     int i, j;
00200 
00201     /* Here is a brain dead merge algorithm which shold make anyone cringe.
00202      * Still, it is simple and we won't merge many attributes and not
00203      * many times either.
00204      */
00205 
00206     for (i = 0 ; i < nsrc ; i++) {
00207        /* see if our destination wants this attribute */
00208        j = 0;
00209        while (j < ndest && strcmp (src[i].name, dest[j].name) != 0)
00210            j++;
00211 
00212        /* if we wanted it and it is the same type, copy it over */
00213        if (j < ndest && src[i].type == dest[j].type) {
00214            dest[j].default_val = src[i].default_val;
00215        }
00216     }
00217 
00218 }
00219 
00220 
00221 /* -------------------------------------------------------------- */
00222 gerbv_image_t *
00223 parse_drillfile(gerb_file_t *fd, gerbv_HID_Attribute *attr_list, int n_attr, int reload)
00224 {
00225     drill_state_t *state = NULL;
00226     gerbv_image_t *image = NULL;
00227     gerbv_net_t *curr_net = NULL;
00228     int read;
00229     gerbv_drill_stats_t *stats;
00230     int i;
00231     gchar *tmps;
00232     gchar *string;
00233 
00234     /* 
00235      * many locales redefine "." as "," and so on, so sscanf and strtod 
00236      * has problems when reading files using %f format.
00237      * Fixes bug #1963618 reported by Lorenzo Marcantonio.
00238      */
00239     setlocale(LC_NUMERIC, "C" );
00240 
00241     /* Create new image for this layer */
00242     dprintf("In parse_drillfile, about to create image for this layer\n");
00243 
00244     image = gerbv_create_image(image, "Excellon Drill File");
00245     if (image == NULL)
00246        GERB_FATAL_ERROR("malloc image failed\n");
00247 
00248     if (reload && attr_list != NULL) {
00249        image->info->attr_list = attr_list;
00250        image->info->n_attr = n_attr;
00251     } else {
00252        /* Copy in the default attribute list for drill files.  We make a
00253         * copy here because we will allow per-layer editing of the
00254         * attributes.
00255         */
00256        image->info->n_attr = sizeof (drill_attribute_list) / sizeof (drill_attribute_list[0]);
00257        image->info->attr_list = (gerbv_HID_Attribute *) malloc (sizeof (drill_attribute_list));
00258        if (image->info->attr_list == NULL) {
00259            fprintf (stderr, "%s():  malloc failed\n", __FUNCTION__);
00260            exit (1);
00261        }
00262        dprintf ("%s(): New attribute list is %p\n", __FUNCTION__, image->info->attr_list);
00263 
00264        for (i = 0 ; i < image->info->n_attr ; i++) {
00265            image->info->attr_list[i] = drill_attribute_list[i];
00266        }
00267 
00268        /* now merge any project attributes */
00269        drill_attribute_merge (image->info->attr_list, image->info->n_attr,
00270                       attr_list, n_attr);
00271     }
00272     
00273     curr_net = image->netlist;
00274     curr_net->layer = image->layers;
00275     curr_net->state = image->states;
00276     image->layertype = GERBV_LAYERTYPE_DRILL;
00277     stats = gerbv_drill_stats_new();
00278     if (stats == NULL)
00279        GERB_FATAL_ERROR("malloc stats failed\n");
00280     image->drill_stats = stats;
00281 
00282     /* Create local state variable to track photoplotter state */
00283     state = new_state(state);
00284     if (state == NULL)
00285        GERB_FATAL_ERROR("malloc state failed\n");
00286 
00287     image->format = (gerbv_format_t *)g_malloc(sizeof(gerbv_format_t));
00288     if (image->format == NULL)
00289        GERB_FATAL_ERROR("malloc format failed\n");
00290     memset((void *)image->format, 0, sizeof(gerbv_format_t));
00291     image->format->omit_zeros = GERBV_OMIT_ZEROS_UNSPECIFIED;
00292 
00293 
00294     if (!image->info->attr_list[HA_auto].default_val.int_value) {
00295        state->autod = 0;
00296        state->number_format = FMT_USER;
00297        state->decimals = image->info->attr_list[HA_digits].default_val.int_value;
00298        if (image->info->attr_list[HA_xy_units].default_val.int_value == UNITS_MM)
00299            state->unit = GERBV_UNIT_MM;
00300        switch (image->info->attr_list[HA_supression].default_val.int_value) {
00301        case SUP_LEAD:
00302            image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
00303            break;
00304            
00305        case SUP_TRAIL:
00306            image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
00307            break;
00308 
00309        default:
00310            image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
00311            break;
00312        }
00313     }
00314 
00315     dprintf("%s():  Starting parsing of drill file\n", __FUNCTION__);
00316     while ((read = gerb_fgetc(fd)) != EOF) {
00317 
00318        switch ((char) read) {
00319        case ';' :
00320            /* Comment found. Eat rest of line */
00321            eat_line(fd);
00322            break;
00323        case 'D' :
00324            gerb_ungetc (fd);
00325            tmps = get_line (fd);
00326            if (strcmp (tmps, "DETECT,ON") == 0 ||
00327               strcmp (tmps, "DETECT,GERBV_APERTURE_STATE_OFF") == 0) {
00328               gchar *tmps2;
00329               gchar *tmps3;
00330               if (strcmp (tmps, "DETECT,ON") == 0)
00331                   tmps3 = "ON";
00332               else
00333                   tmps3 = "GERBV_APERTURE_STATE_OFF";
00334 
00335               /* broken tool detect on/off.  Silently ignored. */
00336               if (stats->detect) {
00337                   tmps2 = g_strdup_printf ("%s\n%s", stats->detect, tmps3);
00338                   g_free (stats->detect);
00339               } else {
00340                   tmps2 = g_strdup_printf ("%s", tmps3);
00341               }
00342               stats->detect = tmps2;
00343            } else {
00344               string = g_strdup_printf("Undefined header line = '%s'\n",tmps);
00345               drill_stats_add_error(stats->error_list,
00346                                   -1,
00347                                   string,
00348                                   GERBV_MESSAGE_NOTE);
00349               g_free(string);
00350            }
00351            g_free (tmps);
00352            break;
00353        case 'F' :
00354            gerb_ungetc (fd);
00355            tmps = get_line (fd);
00356            /* Silently ignore FMAT,2.  Not sure what others are allowed */
00357            if (strcmp (tmps, "FMAT,2") != 0) {
00358               string = g_strdup_printf("Undefined header line = '%s'\n",tmps);
00359               drill_stats_add_error(stats->error_list,
00360                                   -1,
00361                                   string,
00362                                   GERBV_MESSAGE_NOTE);
00363               g_free(string);
00364            }
00365            g_free (tmps);
00366            break;
00367 
00368        case 'G':
00369            /* Most G codes aren't used, for now */
00370            switch(drill_parse_G_code(fd, image)) {
00371            case DRILL_G_ROUT :
00372               drill_stats_add_error(stats->error_list,
00373                                   -1,
00374                                   "Rout mode data is not supported\n",
00375                                   GERBV_MESSAGE_ERROR);
00376               break;
00377            case DRILL_G_DRILL :
00378               break;
00379            case DRILL_G_ABSOLUTE :
00380               state->coordinate_mode = DRILL_MODE_ABSOLUTE;
00381               break;
00382            case DRILL_G_INCREMENTAL :
00383               state->coordinate_mode = DRILL_MODE_INCREMENTAL;
00384               break;
00385            case DRILL_G_ZEROSET :
00386               if((read = gerb_fgetc(fd)) == EOF)
00387               drill_stats_add_error(stats->error_list,
00388                                   -1,
00389                                   "Unexpected EOF found.\n",
00390                                   GERBV_MESSAGE_ERROR);
00391               drill_parse_coordinate(fd, (char)read, image, state);
00392               state->origin_x = state->curr_x;
00393               state->origin_y = state->curr_y;
00394               break;
00395            default :
00396               eat_line(fd);
00397               break;
00398            }
00399            break;
00400        case 'I':
00401           if (state->curr_section != DRILL_HEADER) 
00402               break;
00403           {
00404               int c = gerb_fgetc(fd);
00405               switch (c) {
00406               case 'N':
00407                  if ('C' == gerb_fgetc(fd)) {
00408                      if ('H' == gerb_fgetc(fd)) {
00409                         state->unit = GERBV_UNIT_INCH;
00410                         
00411                         /* Look for TZ/LZ */
00412                         if (',' == gerb_fgetc(fd)) {
00413                             c = gerb_fgetc(fd);
00414                             if (c != EOF && 'Z' == gerb_fgetc(fd)) {
00415                                switch (c) {
00416                                case 'L':
00417                                    if (state->autod) {
00418                                       image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
00419                                       state->header_number_format =
00420                                           state->number_format = FMT_00_0000;
00421                                       state->decimals = 4;
00422                                    }
00423                                    break;
00424 
00425                                case 'T':
00426                                    if (state->autod) {
00427                                       image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
00428                                       state->header_number_format =
00429                                           state->number_format = FMT_00_0000;
00430                                       state->decimals = 4;
00431                                    }
00432                                    break;
00433                                    
00434                                default:
00435                                    drill_stats_add_error(stats->error_list,
00436                                                       -1,
00437                                                       "Found junk after INCH command\n",
00438                                                       GERBV_MESSAGE_WARNING);
00439                                    break;
00440                                }
00441                             } else {
00442                                drill_stats_add_error(stats->error_list,
00443                                                   -1,
00444                                                   "Found junk after INCH command\n",
00445                                                   GERBV_MESSAGE_WARNING);
00446                             }
00447                         }
00448                      }
00449                  }
00450                  break;
00451               case 'C':
00452                  if ('I' == gerb_fgetc(fd))
00453                  if (',' == gerb_fgetc(fd))
00454                  if ('O' == gerb_fgetc(fd)) {
00455                      if ('N' == (c = gerb_fgetc(fd)))
00456                         state->coordinate_mode = DRILL_MODE_INCREMENTAL;
00457                      else if ('F' == c) if ('F' == gerb_fgetc(fd))
00458                         state->coordinate_mode = DRILL_MODE_ABSOLUTE;
00459                  }
00460                  break;
00461               }
00462               eat_line(fd);
00463           }
00464           break;
00465 
00466        case 'M':
00467            switch(drill_parse_M_code(fd, state, image)) {
00468            case DRILL_M_HEADER :
00469               state->curr_section = DRILL_HEADER;
00470               break;
00471            case DRILL_M_ENDHEADER :
00472               state->curr_section = DRILL_DATA;
00473 
00474               if (image->format->omit_zeros == GERBV_OMIT_ZEROS_UNSPECIFIED) {
00475                   /* Excellon says they default to specify leading
00476                      zeros, i.e. omit trailing zeros.    The Excellon
00477                      files floating around that don't specify the
00478                      leading/trailing zeros in the header seem to
00479                      contradict to this though.
00480 
00481                      XXX We should probably ask the user. */
00482 
00483                   drill_stats_add_error(stats->error_list,
00484                                      -1,
00485                                      "End of Excellon header reached but no leading/trailing zero handling specified.\n",
00486                                      GERBV_MESSAGE_ERROR);
00487                   drill_stats_add_error(stats->error_list,
00488                                      -1,
00489                                      "Assuming leading zeros.\n",
00490                                      GERBV_MESSAGE_WARNING);
00491                   image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
00492               }
00493               break;
00494            case DRILL_M_METRIC :
00495               if (state->unit == GERBV_UNIT_UNSPECIFIED &&
00496                   state->curr_section != DRILL_HEADER) {
00497                   drill_stats_add_error(stats->error_list,
00498                                      -1,
00499                                      "M71 code found but no METRIC specification in header.\n",
00500                                      GERBV_MESSAGE_ERROR);
00501                   drill_stats_add_error(stats->error_list,
00502                                      -1,
00503                                      "Assuming all tool sizes are MM.\n",
00504                                      GERBV_MESSAGE_WARNING);
00505                   int tool_num;
00506                   double size;
00507                   stats = image->drill_stats;
00508                   for (tool_num = TOOL_MIN; tool_num < TOOL_MAX; tool_num++) {
00509                      if (image->aperture && image->aperture[tool_num]) {
00510                          /* First update stats.   Do this before changing drill dias.
00511                           * Maybe also put error into stats? */
00512                          size = image->aperture[tool_num]->parameter[0];
00513                          drill_stats_modify_drill_list(stats->drill_list, 
00514                                                    tool_num, 
00515                                                    size, 
00516                                                    "MM");
00517                          /* Now go back and update all tool dias, since
00518                           * tools are displayed in inch units
00519                           */
00520                          image->aperture[tool_num]->parameter[0] /= 25.4;
00521                      }
00522                   }
00523               }
00524               if (state->autod) {
00525                   state->number_format = state->backup_number_format;
00526                   state->unit = GERBV_UNIT_MM;
00527               }
00528               break;
00529            case DRILL_M_IMPERIAL :
00530               if (state->autod) {
00531                   if (state->number_format != FMT_00_0000)
00532                      /* save metric format definition for later */
00533                      state->backup_number_format = state->number_format;
00534                   state->number_format = FMT_00_0000;
00535                   state->decimals = 4;
00536                   state->unit = GERBV_UNIT_INCH;
00537               }
00538 
00539               break;
00540            case DRILL_M_LONGMESSAGE :
00541            case DRILL_M_MESSAGE :
00542            case DRILL_M_CANNEDTEXT :
00543               tmps = get_line(fd);
00544               string = g_strdup_printf("Message embedded in drill file: '%s'\n", 
00545                                     tmps);
00546               drill_stats_add_error(stats->error_list,
00547                                   -1,
00548                                   string,
00549                                   GERBV_MESSAGE_NOTE);
00550               g_free(string);
00551               g_free(tmps);
00552               break;
00553            case DRILL_M_NOT_IMPLEMENTED :
00554            case DRILL_M_ENDPATTERN :
00555            case DRILL_M_TIPCHECK :
00556               break;
00557            case DRILL_M_END :
00558               /* M00 has optional arguments */
00559               eat_line(fd);
00560            case DRILL_M_ENDREWIND :
00561               goto drill_parse_end;
00562               break;
00563            case DRILL_M_METRICHEADER :
00564              state->unit = GERBV_UNIT_MM;
00565              break;
00566            default:
00567               drill_stats_add_error(stats->error_list,
00568                                   -1,
00569                                   "Undefined M code found.\n",
00570                                   GERBV_MESSAGE_ERROR);
00571            }
00572            break;
00573 
00574        case 'S':
00575            drill_stats_add_error(stats->error_list,
00576                               -1,
00577                               "Drill file sets spindle speed -- ignoring.\n",
00578                               GERBV_MESSAGE_NOTE);
00579            eat_line(fd);
00580            break;
00581        case 'T':
00582            drill_parse_T_code(fd, state, image);
00583            break;
00584        case 'V' :
00585            gerb_ungetc (fd);
00586            tmps = get_line (fd);
00587            /* Silently ignore VER,1.  Not sure what others are allowed */
00588            if (strcmp (tmps, "VER,1") != 0) {
00589               string = g_strdup_printf("Undefined header line = '%s'\n",tmps);
00590               drill_stats_add_error(stats->error_list,
00591                                   -1,
00592                                   g_strdup_printf("Undefined header line = '%s'\n",tmps),
00593                                   GERBV_MESSAGE_NOTE);
00594               g_free(string);
00595            }
00596            g_free (tmps);
00597            break;
00598 
00599        case 'X':
00600        case 'Y':
00601            /* Hole coordinate found. Do some parsing */
00602            drill_parse_coordinate(fd, read, image, state);
00603 
00604            /* Add one to drill stats  for the current tool */
00605            drill_stats_increment_drill_counter(image->drill_stats->drill_list,
00606                                           state->current_tool);
00607 
00608            curr_net->next = (gerbv_net_t *)g_malloc(sizeof(gerbv_net_t));
00609            if (curr_net->next == NULL)
00610               GERB_FATAL_ERROR("malloc curr_net->next failed\n");
00611            curr_net = curr_net->next;
00612            memset((void *)curr_net, 0, sizeof(gerbv_net_t));
00613            curr_net->layer = image->layers;
00614            curr_net->state = image->states;
00615            curr_net->start_x = (double)state->curr_x;
00616            curr_net->start_y = (double)state->curr_y;
00617            /* KLUDGE. This function isn't allowed to return anything
00618               but inches */
00619            if(state->unit == GERBV_UNIT_MM) {
00620               curr_net->start_x /= 25.4;
00621               curr_net->start_y /= 25.4;
00622               /* KLUDGE. All images, regardless of input format,
00623                  are returned in INCH format */
00624               curr_net->state->unit = GERBV_UNIT_INCH;
00625            }
00626 
00627            curr_net->stop_x = curr_net->start_x - state->origin_x;
00628            curr_net->stop_y = curr_net->start_y - state->origin_y;
00629            curr_net->aperture = state->current_tool;
00630            curr_net->aperture_state = GERBV_APERTURE_STATE_FLASH;
00631 
00632            /* Find min and max of image.
00633               Mustn't forget (again) to add the hole radius */
00634 
00635            /* Check if aperture is set. Ignore the below instead of
00636               causing SEGV... */
00637            if(image->aperture[state->current_tool] == NULL)
00638               break;
00639 
00640            image->info->min_x =
00641               min(image->info->min_x,
00642                   (curr_net->start_x -
00643                    image->aperture[state->current_tool]->parameter[0] / 2));
00644            image->info->min_y =
00645               min(image->info->min_y,
00646                   (curr_net->start_y -
00647                    image->aperture[state->current_tool]->parameter[0] / 2));
00648            image->info->max_x =
00649               max(image->info->max_x,
00650                   (curr_net->start_x +
00651                    image->aperture[state->current_tool]->parameter[0] / 2));
00652            image->info->max_y =
00653               max(image->info->max_y,
00654                   (curr_net->start_y +
00655                    image->aperture[state->current_tool]->parameter[0] / 2));
00656            break;
00657        case '%':
00658            state->curr_section = DRILL_DATA;
00659            break;
00660        case 10 :   /* White space */
00661        case 13 :
00662        case ' ' :
00663        case '\t' :
00664            break;
00665        default:
00666            if(state->curr_section == DRILL_HEADER) {
00667               /* Unrecognised crap in the header is thrown away */
00668               drill_stats_add_error(stats->error_list,
00669                                   -1,
00670                                   "Undefined codes found in header.\n",
00671                                   GERBV_MESSAGE_ERROR);
00672               gerb_ungetc(fd);
00673               tmps = get_line(fd);
00674               string = g_strdup_printf("Undefined header line = '%s'\n",
00675                                     tmps);
00676               drill_stats_add_error(stats->error_list,
00677                                   -1,
00678                                   string,
00679                                   GERBV_MESSAGE_NOTE);
00680               g_free(string);
00681               g_free (tmps);
00682            } else {
00683               string = g_strdup_printf("Undefined character '%c' [0x%02x] found inside data, ignoring\n",
00684                                     read, read);
00685               drill_stats_add_error(stats->error_list,
00686                                   -1,
00687                                   string,
00688                                   GERBV_MESSAGE_ERROR);
00689               g_free(string);
00690            }
00691        }
00692     }
00693     drill_stats_add_error(stats->error_list,
00694                        -1,
00695                        "No EOF found in drill file.\n",
00696                        GERBV_MESSAGE_ERROR);
00697 
00698  drill_parse_end:
00699     dprintf ("%s():  Populating file attributes\n", __FUNCTION__);
00700 
00701     switch (state->unit) {
00702     case GERBV_UNIT_MM:
00703        image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_MM;
00704        break;
00705            
00706     default:
00707        image->info->attr_list[HA_xy_units].default_val.int_value = UNITS_INCH;
00708        break;
00709     }
00710 
00711     switch (state->number_format) {
00712     case FMT_000_00:
00713     case FMT_0000_00:
00714        image->info->attr_list[HA_digits].default_val.int_value = 2;
00715        break;
00716 
00717     case FMT_000_000:
00718        image->info->attr_list[HA_digits].default_val.int_value = 3;
00719        break;
00720        
00721     case FMT_00_0000:
00722        image->info->attr_list[HA_digits].default_val.int_value = 4;
00723        break;
00724        
00725     case FMT_USER:
00726        dprintf ("%s():  Keeping user specified number of decimal places (%d)\n",
00727                __FUNCTION__,
00728                image->info->attr_list[HA_digits].default_val.int_value);
00729        break;
00730        
00731     default:
00732        break;
00733     }
00734 
00735     switch (image->format->omit_zeros) {
00736     case GERBV_OMIT_ZEROS_LEADING:
00737        image->info->attr_list[HA_supression].default_val.int_value = SUP_LEAD;
00738        break;
00739            
00740     case GERBV_OMIT_ZEROS_TRAILING:
00741        image->info->attr_list[HA_supression].default_val.int_value = SUP_TRAIL;
00742        break;
00743 
00744     default:
00745        image->info->attr_list[HA_supression].default_val.int_value = SUP_NONE;
00746        break;
00747     }
00748 
00749     g_free(state);
00750 
00751     return image;
00752 } /* parse_drillfile */
00753 
00754 
00755 /* -------------------------------------------------------------- */
00756 /*
00757  * Checks for signs that this is a drill file
00758  * Returns TRUE if it is, FALSE if not.
00759  */
00760 gboolean
00761 drill_file_p(gerb_file_t *fd, gboolean *returnFoundBinary)
00762 {
00763     char *buf;
00764     int len = 0;
00765     char *letter;
00766     int ascii;
00767     int zero = 48; /* ascii 0 */
00768     int nine = 57; /* ascii 9 */
00769     int i;
00770     gboolean found_binary = FALSE;
00771     gboolean found_M48 = FALSE;
00772     gboolean found_M30 = FALSE;
00773     gboolean found_percent = FALSE;
00774     gboolean found_T = FALSE;
00775     gboolean found_X = FALSE;
00776     gboolean found_Y = FALSE;
00777  
00778     buf = g_malloc(MAXL);
00779     if (buf == NULL) 
00780        GERB_FATAL_ERROR("malloc buf failed while checking for drill file.\n");
00781 
00782     while (fgets(buf, MAXL, fd->fd) != NULL) {
00783        len = strlen(buf);
00784 
00785        /* First look through the file for indications of its type */
00786 
00787        /* check that file is not binary (non-printing chars) */
00788        for (i = 0; i < len; i++) {
00789            ascii = (int) buf[i];
00790            if ((ascii > 128) || (ascii < 0)) {
00791               found_binary = TRUE;
00792            }
00793        }
00794 
00795        /* Check for M48 = start of drill header */
00796        if (g_strstr_len(buf, len, "M48")) {
00797            found_M48 = TRUE; 
00798        }
00799 
00800        /* Check for M30 = end of drill program */
00801        if (g_strstr_len(buf, len, "M30")) {
00802            if (found_percent) {
00803               found_M30 = TRUE; /* Found M30 after % = good */
00804            }
00805        }
00806 
00807        /* Check for % on its own line at end of header */
00808        if ((letter = g_strstr_len(buf, len, "%")) != NULL) {
00809            if ((letter[1] ==  '\r') || (letter[1] ==  '\n'))
00810               found_percent = TRUE;
00811        }
00812 
00813        /* Check for T<number> */
00814        if ((letter = g_strstr_len(buf, len, "T")) != NULL) {
00815            if (!found_T && (found_X || found_Y)) {
00816               found_T = FALSE;  /* Found first T after X or Y */
00817            } else {
00818               if (isdigit( (int) letter[1])) { /* verify next char is digit */
00819                   found_T = TRUE;
00820               }
00821            }
00822        }
00823 
00824        /* look for X<number> or Y<number> */
00825        if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
00826            ascii = (int) letter[1]; /* grab char after X */
00827            if ((ascii >= zero) && (ascii <= nine)) {
00828               found_X = TRUE;
00829            }
00830        }
00831        if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
00832            ascii = (int) letter[1]; /* grab char after Y */
00833            if ((ascii >= zero) && (ascii <= nine)) {
00834               found_Y = TRUE;
00835            }
00836        }
00837     } /* while (fgets(buf, MAXL, fd->fd) */
00838 
00839     rewind(fd->fd);
00840     free(buf);
00841     *returnFoundBinary = found_binary;
00842     
00843     /* Now form logical expression determining if this is a drill file */
00844     if ( ((found_X || found_Y) && found_T) && 
00845         (found_M48 || (found_percent && found_M30)) ) 
00846        return TRUE;
00847     else if (found_M48 && found_T && found_percent && found_M30)
00848        /* Pathological case of drill file with valid header 
00849           and EOF but no drill XY locations. */
00850        return TRUE;
00851     else 
00852        return FALSE;
00853 } /* drill_file_p */
00854 
00855 
00856 /* -------------------------------------------------------------- */
00857 /* Parse tool definition. This can get a bit tricky since it can
00858    appear in the header and/or data section.
00859    Returns tool number on success, -1 on error */
00860 static int
00861 drill_parse_T_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
00862 {
00863     int tool_num;
00864     gboolean done = FALSE;
00865     int temp;
00866     double size;
00867     gerbv_drill_stats_t *stats = image->drill_stats;
00868     gchar *tmps;
00869     gchar *string;
00870 
00871     /* Sneak a peek at what's hiding after the 'T'. Ugly fix for
00872        broken headers from Orcad, which is crap */
00873     temp = gerb_fgetc(fd);
00874     dprintf("Found a char %d after the T\n", temp);
00875     
00876     /* might be a tool tool change stop switch on/off*/
00877     if((temp == 'C') && ((fd->ptr + 2) < fd->datalen)){
00878        if(gerb_fgetc(fd) == 'S'){
00879            if (gerb_fgetc(fd) == 'T' ){
00880               fd->ptr -= 4;
00881               tmps = get_line(fd++);
00882               string = g_strdup_printf("Tool change stop switch found: %s\n", tmps);
00883               drill_stats_add_error(stats->error_list, 
00884                                   -1,
00885                                   string,
00886                                   GERBV_MESSAGE_NOTE);
00887               g_free(string);
00888               g_free (tmps);
00889               return -1;
00890            }
00891            gerb_ungetc(fd);
00892        }
00893        gerb_ungetc(fd);
00894     }
00895 
00896     if( !(isdigit(temp) != 0 || temp == '+' || temp =='-') ) {
00897        if(temp != EOF) {
00898            drill_stats_add_error(stats->error_list,
00899                               -1,
00900                               "Orcad bug: Junk text found in place of tool definition.\n",
00901                               GERBV_MESSAGE_ERROR);
00902            tmps = get_line(fd);
00903            string = g_strdup_printf("Junk text = %s\n", 
00904                                  tmps);
00905            drill_stats_add_error(stats->error_list,
00906                               -1,
00907                               string,
00908                               GERBV_MESSAGE_NOTE);
00909            g_free(string);
00910            g_free (tmps);
00911            drill_stats_add_error(stats->error_list,
00912                               -1,
00913                               "Ignorning junk text.\n",
00914                               GERBV_MESSAGE_WARNING);
00915        }
00916        return -1;
00917     }
00918     gerb_ungetc(fd);
00919 
00920     tool_num = (int) gerb_fgetint(fd, NULL);
00921     dprintf ("In %s: handling tool_num = %d\n", __FUNCTION__, tool_num);
00922 
00923     if (tool_num == 0) 
00924        return tool_num; /* T00 is a command to unload the drill */
00925 
00926     if ( (tool_num < TOOL_MIN) || (tool_num >= TOOL_MAX) ) {
00927        string = g_strdup_printf("Drill number out of bounds: %d.\n", tool_num);
00928        drill_stats_add_error(stats->error_list,
00929                            -1,
00930                            string,
00931                            GERBV_MESSAGE_ERROR);
00932        g_free(string);
00933     }
00934 
00935     /* Set the current tool to the correct one */
00936     state->current_tool = tool_num;
00937 
00938     /* Check for a size definition */
00939     temp = gerb_fgetc(fd);
00940 
00941     /* This bit of code looks for a tool definition by scanning for strings
00942      * of form TxxC, TxxF, TxxS.  */
00943     while(!done) {
00944        
00945        switch((char)temp) {
00946        case 'C':
00947            size = read_double(fd, state->header_number_format, GERBV_OMIT_ZEROS_TRAILING, state->decimals);
00948            dprintf ("%s: Read a size of %g %s\n", __FUNCTION__, size,
00949                    state->unit == GERBV_UNIT_MM ? "mm" : "inch");
00950            if(state->unit == GERBV_UNIT_MM) {
00951               size /= 25.4;
00952            } else if(size >= 4.0) {
00953               /* If the drill size is >= 4 inches, assume that this
00954                  must be wrong and that the units are mils.
00955                  The limit being 4 inches is because the smallest drill
00956                  I've ever seen used is 0,3mm(about 12mil). Half of that
00957                  seemed a bit too small a margin, so a third it is */
00958               string = g_strdup_printf("Read a drill of diameter %g inches.\n", size);
00959               drill_stats_add_error(stats->error_list,
00960                                   -1,
00961                                   string,
00962                                   GERBV_MESSAGE_ERROR); 
00963               g_free(string);
00964               string = g_strdup_printf("Assuming units are mils.\n");
00965               drill_stats_add_error(stats->error_list,
00966                                   -1,
00967                                   string,
00968                                   GERBV_MESSAGE_WARNING); 
00969               g_free(string);
00970               size /= 1000.0;
00971            }
00972 
00973            if(size <= 0. || size >= 10000.) {
00974               string = g_strdup_printf("Unreasonable drill size found for drill %d: %g\n", tool_num, size);
00975               drill_stats_add_error(stats->error_list,
00976                                   -1,
00977                                   string,
00978                                   GERBV_MESSAGE_ERROR);
00979               g_free(string);
00980            } else {
00981               if(image->aperture[tool_num] != NULL) {
00982                   /* allow a redefine of a tool only if the new definition is exactly the same.
00983                    * This avoid lots of spurious complaints with the output of some cad
00984                    * tools while keeping complaints if there is a true problem
00985                    */
00986                   if (image->aperture[tool_num]->parameter[0] != size ||
00987                      image->aperture[tool_num]->type != GERBV_APTYPE_CIRCLE ||
00988                      image->aperture[tool_num]->nuf_parameters != 1 ||
00989                      image->aperture[tool_num]->unit != GERBV_UNIT_INCH) {
00990                      string = g_strdup_printf("Found redefinition of drill %d.\n", tool_num);
00991                      drill_stats_add_error(stats->error_list,
00992                                          -1,
00993                                          string,
00994                                          GERBV_MESSAGE_ERROR);
00995                      g_free(string);
00996                   }
00997               } else {
00998                   image->aperture[tool_num] =
00999                      (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
01000                   if (image->aperture[tool_num] == NULL) {
01001                      GERB_FATAL_ERROR("malloc tool failed\n");
01002                   }
01003                   /* make sure we zero out all aperature parameters */
01004                   memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
01005                   /* There's really no way of knowing what unit the tools
01006                      are defined in without sneaking a peek in the rest of
01007                      the file first. That's done in drill_guess_format() */
01008                   image->aperture[tool_num]->parameter[0] = size;
01009                   image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
01010                   image->aperture[tool_num]->nuf_parameters = 1;
01011                   image->aperture[tool_num]->unit = GERBV_UNIT_INCH;
01012               }
01013            }
01014            
01015            /* Add the tool whose definition we just found into the list
01016             * of tools for this layer used to generate statistics. */
01017            stats = image->drill_stats;
01018            string = g_strdup_printf("%s", (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
01019            drill_stats_add_to_drill_list(stats->drill_list, 
01020                                      tool_num, 
01021                                      state->unit == GERBV_UNIT_MM ? size*25.4 : size, 
01022                                      string);
01023            g_free(string);
01024            break;
01025 
01026        case 'F':
01027        case 'S' :
01028            /* Silently ignored. They're not important. */
01029            gerb_fgetint(fd, NULL);
01030            break;
01031 
01032        default:
01033            /* Stop when finding anything but what's expected
01034               (and put it back) */
01035            gerb_ungetc(fd);
01036            done = TRUE;
01037            break;
01038        }  /* switch((char)temp) */
01039 
01040        if( (temp = gerb_fgetc(fd)) == EOF) {
01041            drill_stats_add_error(stats->error_list,
01042                               -1,
01043                               "Unexpected EOF encountered header of drill file.\n",
01044                               GERBV_MESSAGE_ERROR);
01045        }
01046     }   /* while(!done) */  /* Done looking at tool definitions */
01047 
01048     /* Catch the tools that aren't defined.
01049        This isn't strictly a good thing, but at least something is shown */
01050     if(image->aperture[tool_num] == NULL) {
01051         double dia;
01052 
01053        image->aperture[tool_num] =
01054            (gerbv_aperture_t *)g_malloc(sizeof(gerbv_aperture_t));
01055        if (image->aperture[tool_num] == NULL) {
01056            GERB_FATAL_ERROR("malloc tool failed\n");
01057        }
01058        /* make sure we zero out all aperature parameters */
01059        memset((void *)image->aperture[tool_num], 0, sizeof(gerbv_aperture_t));
01060 
01061         /* See if we have the tool table */
01062         dia = gerbv_get_tool_diameter(tool_num);
01063         if (dia <= 0) {
01064             /*
01065              * There is no tool. So go out and make some.
01066              * This size calculation is, of course, totally bogus.
01067              */
01068             dia = (double)(16 + 8 * tool_num) / 1000;
01069             /*
01070              * Oooh, this is sooo ugly. But some CAD systems seem to always
01071              * use T00 at the end of the file while others that don't have
01072              * tool definitions inside the file never seem to use T00 at all.
01073              */
01074             if(tool_num != 0) {
01075               string = g_strdup_printf("Tool %02d used without being defined\n", tool_num);
01076               drill_stats_add_error(stats->error_list,
01077                                   -1,
01078                                   string,
01079                                   GERBV_MESSAGE_ERROR);
01080               g_free(string);
01081               string = g_strdup_printf("Setting a default size of %g\"\n", dia);
01082               drill_stats_add_error(stats->error_list,
01083                                   -1,
01084                                   string,
01085                                   GERBV_MESSAGE_WARNING);
01086               g_free(string);
01087             }
01088        }
01089 
01090        image->aperture[tool_num]->type = GERBV_APTYPE_CIRCLE;
01091        image->aperture[tool_num]->nuf_parameters = 1;
01092        image->aperture[tool_num]->parameter[0] = dia;
01093 
01094        /* Add the tool whose definition we just found into the list
01095         * of tools for this layer used to generate statistics. */
01096        if (tool_num != 0) {  /* Only add non-zero tool nums.  
01097                             * Zero = unload command. */
01098            stats = image->drill_stats;
01099            string = g_strdup_printf("%s", 
01100                                  (state->unit == GERBV_UNIT_MM ? "mm" : "inch"));
01101            drill_stats_add_to_drill_list(stats->drill_list, 
01102                                      tool_num, 
01103                                      state->unit == GERBV_UNIT_MM ? dia*25.4 : dia,
01104                                      string);
01105            g_free(string);
01106        }
01107     } /* if(image->aperture[tool_num] == NULL) */       
01108     
01109     return tool_num;
01110 } /* drill_parse_T_code */
01111 
01112 
01113 /* -------------------------------------------------------------- */
01114 static int
01115 drill_parse_M_code(gerb_file_t *fd, drill_state_t *state, gerbv_image_t *image)
01116 {
01117     char op[3] = "  ";
01118     int  read[3];
01119     gerbv_drill_stats_t *stats = image->drill_stats;
01120     int result=0;
01121 
01122     dprintf("---> entering drill_parse_M_code ...\n");
01123 
01124     read[0] = gerb_fgetc(fd);
01125     read[1] = gerb_fgetc(fd);
01126 
01127     if ((read[0] == EOF) || (read[1] == EOF))
01128        drill_stats_add_error(stats->error_list,
01129                            -1,
01130                            "Unexpected EOF found while parsing M code.\n",
01131                            GERBV_MESSAGE_ERROR);
01132     op[0] = read[0], op[1] = read[1], op[2] = 0;
01133  
01134     if (strncmp(op, "00", 2) == 0) {
01135        stats->M00++;
01136        result = DRILL_M_END;
01137     } else if (strncmp(op, "01", 2) == 0) {
01138        stats->M01++;
01139        result = DRILL_M_ENDPATTERN;
01140     } else if (strncmp(op, "18", 2) == 0) {
01141        stats->M18++;
01142        result = DRILL_M_TIPCHECK;
01143     } else if (strncmp(op, "25", 2) == 0) {
01144        stats->M25++;
01145        result = DRILL_M_BEGINPATTERN;
01146     } else if (strncmp(op, "31", 2) == 0) {
01147        stats->M31++;
01148        result = DRILL_M_BEGINPATTERN;
01149     } else if (strncmp(op, "30", 2) == 0) {
01150        stats->M30++;
01151        result = DRILL_M_ENDREWIND;
01152     } else if (strncmp(op, "45", 2) == 0) {
01153        stats->M45++;
01154        result = DRILL_M_LONGMESSAGE;
01155     } else if (strncmp(op, "47", 2) == 0) {
01156        stats->M47++;
01157        result = DRILL_M_MESSAGE;
01158     } else if (strncmp(op, "48", 2) == 0) {
01159        stats->M48++;
01160        result = DRILL_M_HEADER;
01161     } else if (strncmp(op, "71", 2) == 0) {
01162        eat_line(fd);
01163        stats->M71++;
01164        result = DRILL_M_METRIC;
01165     } else if (strncmp(op, "72", 2) == 0) {
01166        eat_line(fd);
01167        stats->M72++;
01168        result = DRILL_M_IMPERIAL;
01169     } else if (strncmp(op, "95", 2) == 0) {
01170        stats->M95++;
01171        result = DRILL_M_ENDHEADER;
01172     } else if (strncmp(op, "97", 2) == 0) {
01173        stats->M97++;
01174        result = DRILL_M_CANNEDTEXT;
01175     } else if (strncmp(op, "98", 2) == 0) {
01176        stats->M98++;
01177        return DRILL_M_CANNEDTEXT;
01178     } else if (state->curr_section == DRILL_HEADER &&
01179               strncmp(op, "ET", 2) == 0) {
01180        /* METRIC is not an actual M code but a command that is only
01181           acceptable within the header.
01182 
01183           The syntax is
01184           METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}]
01185        */
01186        if ('R' == gerb_fgetc(fd) &&
01187            'I' == gerb_fgetc(fd) &&
01188            'C' == gerb_fgetc(fd)) {
01189        again:
01190            if (',' == gerb_fgetc(fd)) {
01191               int c;
01192 
01193               /* Is it tzlz, or zerofmt? */
01194               switch ((c = gerb_fgetc(fd))) {
01195               case 'T':
01196               case 'L':
01197                   if ('Z' != gerb_fgetc(fd))
01198                      goto junk;
01199                   if (c == 'L')
01200                      {
01201                          dprintf ("%s(): Detected a file that probably has trailing zero supression\n", __FUNCTION__);
01202                          if (state->autod)
01203                             {
01204                                 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
01205                             }
01206                      }
01207                   else
01208                      {
01209                          dprintf ("%s(): Detected a file that probably has leading zero supression\n", __FUNCTION__);
01210                          if (state->autod)
01211                             {
01212                                 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
01213                             }
01214                      }
01215                   if (state->autod)
01216                      {
01217                          /* Default metric number format is 6-digit, 1 um
01218                             resolution.  The header number format (for T#C#
01219                             definitions) is fixed to that, while the number
01220                             format within the file can differ. */
01221                          state->header_number_format =
01222                             state->number_format = FMT_000_000;
01223                          state->decimals = 3;
01224                      }
01225                   c = gerb_fgetc(fd);
01226                   gerb_ungetc(fd);
01227                   if (c == ',')
01228                      /* anticipate number format will follow */
01229                      goto again;
01230                   break;
01231 
01232               case '0':
01233                   if ('0' != gerb_fgetc(fd) ||
01234                      '0' != gerb_fgetc(fd))
01235                      goto junk;
01236                   /* We just parsed three 0s, the remainder options
01237                      so far are: .000 | .00 | 0.00 */
01238                   read[0] = gerb_fgetc(fd);
01239                   read[1] = gerb_fgetc(fd);
01240                   if (read[0] == EOF || read[1] == EOF)
01241                      goto junk;
01242                   op[0] = read[0];
01243                   op[1] = read[1];
01244                   if (strcmp(op, "0.") == 0) {
01245                      /* expecting FMT_0000_00,
01246                         two trailing 0s must follow */
01247                      if ('0' != gerb_fgetc(fd) ||
01248                          '0' != gerb_fgetc(fd))
01249                          goto junk;
01250                      eat_line(fd);
01251                      if (state->autod)
01252                          {
01253                             state->number_format = FMT_0000_00;
01254                             state->decimals = 2;
01255                          }
01256                      break;
01257                   }
01258                   if (strcmp(op, ".0") != 0)
01259                      goto junk;
01260                   /* must be either FMT_000_000 or FMT_000_00, depending
01261                      on whether one or two 0s are following */
01262                   if ('0' != gerb_fgetc(fd))
01263                      goto junk;
01264                   if ('0' == gerb_fgetc(fd) && state->autod)
01265                      {
01266                          state->number_format = FMT_000_000;
01267                          state->decimals = 3;
01268                      }
01269                   else {
01270                      gerb_ungetc(fd);
01271                      if (state->autod)
01272                          {
01273                             state->number_format = FMT_000_00;
01274                             state->decimals = 2;
01275                          }
01276                   }
01277                   eat_line(fd);
01278                   break;
01279 
01280               default:
01281               junk:
01282                   drill_stats_add_error(stats->error_list,
01283                                      -1,
01284                                      "Found junk after METRIC command\n",
01285                                      GERBV_MESSAGE_WARNING);
01286                   gerb_ungetc(fd);
01287                   eat_line(fd);
01288                   break;
01289               }
01290            } else {
01291               gerb_ungetc(fd);
01292               eat_line(fd);
01293            }
01294 
01295            return DRILL_M_METRICHEADER;
01296        }
01297     } else {
01298        stats->M_unknown++;
01299        result = DRILL_M_UNKNOWN;
01300     }
01301 
01302     dprintf("<----  ...leaving drill_parse_M_code.\n");
01303     return result;
01304 } /* drill_parse_M_code */
01305 
01306 
01307 /* -------------------------------------------------------------- */
01308 static int
01309 drill_parse_G_code(gerb_file_t *fd, gerbv_image_t *image)
01310 {
01311     char op[3] = "  ";
01312     int  read[3];
01313     gerbv_drill_stats_t *stats = image->drill_stats;
01314     int result;
01315     
01316     dprintf("---> entering drill_parse_G_code ...\n");
01317 
01318     read[0] = gerb_fgetc(fd);
01319     read[1] = gerb_fgetc(fd);
01320 
01321     if ((read[0] == EOF) || (read[1] == EOF)) {
01322        drill_stats_add_error(stats->error_list,
01323                            -1,
01324                            "Unexpected EOF found while parsing G code.\n",
01325                            GERBV_MESSAGE_ERROR);
01326     }
01327 
01328     op[0] = read[0], op[1] = read[1], op[2] = 0;
01329 
01330     if (strncmp(op, "00", 2) == 0) {
01331        stats->G00++;
01332        result = DRILL_G_ROUT;
01333     } else if (strncmp(op, "01", 2) == 0) {
01334        stats->G01++;
01335        result = DRILL_G_LINEARMOVE;
01336     } else if (strncmp(op, "02", 2) == 0) {
01337        stats->G02++;
01338        result = DRILL_G_CWMOVE;
01339     } else if (strncmp(op, "03", 2) == 0) {
01340        stats->G03++;
01341        result = DRILL_G_CCWMOVE;
01342     } else if (strncmp(op, "05", 2) == 0) {
01343        stats->G05++;
01344        result = DRILL_G_DRILL;
01345     } else if (strncmp(op, "90", 2) == 0) {
01346        stats->G90++;
01347        result = DRILL_G_ABSOLUTE;
01348     } else if (strncmp(op, "91", 2) == 0) {
01349        stats->G91++;
01350        result = DRILL_G_INCREMENTAL;
01351     } else if (strncmp(op, "93", 2) == 0) {
01352        stats->G93++;
01353        result = DRILL_G_ZEROSET;
01354     } else {  
01355        stats->G_unknown++;
01356        result = DRILL_G_UNKNOWN;
01357     }
01358 
01359     dprintf("<----  ...leaving drill_parse_G_code.\n");
01360     return result;
01361 
01362 } /* drill_parse_G_code */
01363 
01364 
01365 /* -------------------------------------------------------------- */
01366 /* Parse on drill file coordinate.
01367    Returns nothing, but modifies state */
01368 static void
01369 drill_parse_coordinate(gerb_file_t *fd, char firstchar,
01370                      gerbv_image_t *image, drill_state_t *state)
01371 
01372 {
01373     int read;
01374 
01375     if(state->coordinate_mode == DRILL_MODE_ABSOLUTE) {
01376        if(firstchar == 'X') {
01377            state->curr_x = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
01378            if((read = (char)gerb_fgetc(fd)) == 'Y') {
01379               state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
01380            }
01381        } else {
01382            state->curr_y = read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
01383        }
01384     } else if(state->coordinate_mode == DRILL_MODE_INCREMENTAL) {
01385        if(firstchar == 'X') {
01386            state->curr_x += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
01387            if((read = (char)gerb_fgetc(fd)) == 'Y') {
01388               state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
01389            }
01390        } else {
01391            state->curr_y += read_double(fd, state->number_format, image->format->omit_zeros, state->decimals);
01392        }
01393     }
01394 
01395 } /* drill_parse_coordinate */
01396 
01397 
01398 /* Allocates and returns a new drill_state structure
01399    Returns state pointer on success, NULL on ERROR */
01400 static drill_state_t *
01401 new_state(drill_state_t *state)
01402 {
01403     state = (drill_state_t *)g_malloc(sizeof(drill_state_t));
01404     if (state != NULL) {
01405        /* Init structure */
01406        memset((void *)state, 0, sizeof(drill_state_t));
01407        state->curr_section = DRILL_NONE;
01408        state->coordinate_mode = DRILL_MODE_ABSOLUTE;
01409        state->origin_x = 0.0;
01410        state->origin_y = 0.0;
01411        state->unit = GERBV_UNIT_UNSPECIFIED;
01412        state->backup_number_format = FMT_000_000; /* only used for METRIC */
01413        state->header_number_format = state->number_format = FMT_00_0000; /* i. e. INCH */
01414        state->autod = 1;
01415        state->decimals = 4;
01416 
01417     }
01418     return state;
01419 } /* new_state */
01420 
01421 
01422 /* -------------------------------------------------------------- */
01423 /* Reads one double from fd and returns it.
01424    If a decimal point is found, fmt is not used. */
01425 static double
01426 read_double(gerb_file_t *fd, enum number_fmt_t fmt, gerbv_omit_zeros_t omit_zeros, int decimals)
01427 {
01428     int read;
01429     char temp[0x20];
01430     int i = 0, ndigits = 0;
01431     double result;
01432     gboolean decimal_point = FALSE;
01433 
01434     memset(temp, 0, sizeof(temp));
01435 
01436     read = gerb_fgetc(fd);
01437     while(read != EOF && i < sizeof(temp) &&
01438          (isdigit(read) || read == '.' || read == ',' || read == '+' || read == '-')) {
01439        if(read == ',' || read == '.') decimal_point = TRUE;
01440        if(read == ',')
01441            read = '.'; /* adjust for strtod() */
01442        if(isdigit(read)) ndigits++;
01443        temp[i++] = (char)read;
01444        read = gerb_fgetc(fd);
01445     }
01446     temp[i] = 0;
01447 
01448     gerb_ungetc(fd);
01449     if (decimal_point) {
01450        result = strtod(temp, NULL);
01451     } else {
01452        int wantdigits;
01453        double scale;
01454 
01455        /* Nothing to take care for when leading zeros are
01456           omitted. */
01457        if (omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
01458            switch (fmt) {
01459            case FMT_00_0000:
01460            case FMT_000_000:
01461            case FMT_0000_00:
01462               wantdigits = 6;
01463               break;
01464 
01465            case FMT_000_00:
01466               wantdigits = 5;
01467               break;
01468 
01469            default:
01470               /* cannot happen, just plugs a compiler warning */
01471               return 0;
01472            }
01473 
01474            /* fill missing trailing digits */
01475            while (ndigits < wantdigits) {
01476               temp[i++] = '0';
01477               ndigits++;
01478            }
01479            temp[i] = 0;
01480        }
01481 
01482        switch (fmt) {
01483        case FMT_00_0000:
01484            scale = 1E-4;
01485            break;
01486 
01487        case FMT_000_000:
01488            scale = 1E-3;
01489            break;
01490 
01491        case FMT_000_00:
01492        case FMT_0000_00:
01493            scale = 1E-2;
01494            break;
01495 
01496        case FMT_USER:
01497            scale = pow (10.0, -1.0*decimals);
01498            break;
01499 
01500        default:
01501            /* cannot happen, just plugs a compiler warning */
01502            fprintf (stderr, "%s(): Unhandled fmt ` %d\n", __FUNCTION__, fmt);
01503            exit (1);
01504        }
01505 
01506        result = strtod(temp, NULL) * scale;
01507     }
01508 
01509     return result;
01510 } /* read_double */
01511 
01512 
01513 /* -------------------------------------------------------------- */
01514 /* Eats all characters up to and including 
01515    the first one of CR or LF */
01516 static void
01517 eat_line(gerb_file_t *fd)
01518 {
01519     int read = gerb_fgetc(fd);
01520     
01521     while(read != 10 && read != 13) {
01522        if (read == EOF) return;
01523        read = gerb_fgetc(fd);
01524     }
01525 } /* eat_line */
01526 
01527 
01528 /* -------------------------------------------------------------- */
01529 static char *
01530 get_line(gerb_file_t *fd)
01531 {
01532     int read = gerb_fgetc(fd);
01533     gchar *retstring = "";
01534     gchar *tmps = NULL;
01535 
01536     while(read != 10 && read != 13) {
01537        if (read == EOF) return retstring;
01538        retstring = g_strdup_printf("%s%c", retstring, read);
01539 
01540        /* since g_strdup_printf allocates memory, we need to free it */
01541        if (tmps)  {
01542            g_free (tmps);
01543            tmps = NULL;
01544        }
01545        tmps = retstring;;
01546        read = gerb_fgetc(fd);
01547     }
01548     return retstring;
01549 } /* get_line */

Generated on Tue Aug 19 00:14:48 2008 for gerbv by  doxygen 1.5.6