cae-tools/cameo/excellon.c

306 lines
5.4 KiB
C

/*
* excellon.c - KiCad drill file input
*
* Written 2010-2012 by Werner Almesberger
* Copyright 2010-2012 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 <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#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, const char *name)
{
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;
case 90: /* absolute mode */
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, name);
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, name);
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, const char *name)
{
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, name);
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, name))
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, name);
fclose(file);
return paths;
}