/* * excellon.c - KiCad drill file input * * Written 2010-2011 by Werner Almesberger * Copyright 2010-2011 Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ /* * KiCad drill files are in the Excellon format. The format is described here: * * http://www.excellon.com/manuals/program.htm * * Note that drl2gp currently only implements the subset necessary to process * the KiCad drill files encountered in a few projects, may not work correctly * for drill files from other projects, and will almost certainly fail in * tool-shattering ways when fed excellon files from other sources. */ #include #include #include #include #include "excellon.h" #define MAX_TOOL 20 static double tool_d[MAX_TOOL+1]; static int lineno = 1; static struct path *paths, **anchor; #define IN2MM(in) ((in)*25.4) #define MIL2MM(mil) IN2MM((mil)/1000) /* ----- header parsing ---------------------------------------------------- */ static void header(FILE *file) { enum { ts_nl, /* at beginning of line */ ts_t, /* seen a ^T */ ts_metric, /* parsing METRIC,... */ ts_inch, /* parsing INCH,... */ ts_tc, /* seen ^T\d+C */ ts_skip, /* skip to next \n */ } state = ts_nl; int c, tool; double f = 1; double tmp; const char *next = NULL; int metric = 1; while ((c = fgetc(file)) != EOF) { switch (state) { case ts_nl: switch (c) { case 'T': state = ts_t; tool = 0; break; case 'I': state = ts_inch; next = "NCH"; break; case 'M': state = ts_metric; next = "ETRIC"; break; case '%': return; case '\n': lineno++; break; default: state = ts_skip; break; } break; case ts_inch: if (c == ',') metric = 0; else { if (c == *next++) break; } state = ts_skip; break; case ts_metric: if (c == ',') metric = 1; else { if (c == *next++) break; } state = ts_skip; break; case ts_t: if (isdigit(c)) tool = tool*10+c-'0'; else if (c != 'C') state = ts_skip; else { assert(c != '\n'); if (tool > MAX_TOOL) { fprintf(stderr, "tool index %d too large (> %d)\n", tool, MAX_TOOL); exit(1); } tool_d[tool] = 0; f = 1; state = ts_tc; } break; case ts_tc: if (isdigit(c)) { tmp = c-'0'; if (!metric) tmp = IN2MM(tmp); if (f == 1) tool_d[tool] = tool_d[tool]*10+tmp; else { tool_d[tool] += f*tmp; f /= 10; } } else if (c == '.') { f = 0.1; } else if (c == '\n') { lineno++; state = ts_nl; } else { state = ts_skip; } break; default: if (c == '\n') { lineno++; state = ts_nl; } else { state = ts_skip; } } } } /* ----- body parsing ------------------------------------------------------ */ static int do_cmd(char cmd, double v, int nl) { static int metric = 1; static int slotting = 0; static double x = 0, y = 0, x0 = 0, d = 0; int n = v; switch (cmd) { case 'M': switch (n) { case 30: /* end of program */ return 0; case 47: /* operator message */ break; case 71: /* metric measuring mode */ metric = 1; break; case 72: /* inch measuring mode */ metric = 0; break; default: fprintf(stderr, "unrecognized command M%d at line %d\n", n, lineno); exit(1); } break; case 'G': switch (n) { case 5: /* drill mode */ break; case 85: /* slot */ x0 = x; slotting = 1; break; default: fprintf(stderr, "unrecognized command G%d at line %d\n", n, lineno); exit(1); } break; case 'T': if (!n) { d = 0; break; } if (n < 1 || n > MAX_TOOL || !tool_d[n]) { fprintf(stderr, "invalid tool T%d at line %d\n", n, lineno); exit(1); } d = tool_d[n]; break; case 'X': x = metric ? v : IN2MM(v); break; case 'Y': if (!metric) v = IN2MM(v); if (slotting) { *anchor = path_new(d/2); path_add(*anchor, x0, y, 0); path_add(*anchor, x, v, 0); anchor = &(*anchor)->next; slotting = 0; break; } if (nl) { assert(d); *anchor = path_new(d/2); path_add(*anchor, x, v, 0); anchor = &(*anchor)->next; break; } y = v; break; default: fprintf(stderr, "unrecognized command \"%c\" at line %d\n", cmd, lineno); exit(1); } return 1; } static void body(FILE *file) { char cmd = 0; double v = 0, f = 1; int c, s = 0; while ((c = fgetc(file)) != EOF) { if (c == '\n') { lineno++; if (cmd) do_cmd(cmd, s ? -v : v, 1); cmd = 0; } else if (isdigit(c)) { if (f == 1) v = v*10+c-'0'; else { v += f*(c-'0'); f /= 10; } } else if (c == '.') { f = 0.1; } else if (c == '-') { s = !s; } else { if (cmd) if (!do_cmd(cmd, s ? -v : v, 0)) return; cmd = c; v = 0; f = 1; s = 0; } } } struct path *excellon_read(const char *name) { FILE *file; file = name ? fopen(name, "r") : stdin; if (!file) { perror(name); exit(1); } lineno = 1; paths = NULL; anchor = &paths; header(file); body(file); fclose(file); return paths; }