1
0
mirror of git://projects.qi-hardware.com/eda-tools.git synced 2024-11-05 05:49:42 +02:00
eda-tools/sch2fig/sch.c
2016-07-30 21:07:36 -03:00

610 lines
12 KiB
C

/*
* sch.c - Parse Eeschema .sch file
*
* Written 2016 by Werner Almesberger
* Copyright 2016 by 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.
*/
#define _GNU_SOURCE /* for asprintf */
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "misc.h"
#include "style.h"
#include "fig.h"
#include "dwg.h"
#include "lib.h"
#include "sch.h"
/* ----- (Global) Labels --------------------------------------------------- */
static enum fig_shape do_decode_shape(const char *s)
{
if (!strcmp(s, "UnSpc"))
return fig_unspec;
if (!strcmp(s, "Input"))
return fig_in;
if (!strcmp(s, "Output"))
return fig_out;
if (!strcmp(s, "3State"))
return fig_tri;
if (!strcmp(s, "BiDi"))
return fig_bidir;
fprintf(stderr, "unknown shape: \"%s\"\n", s);
exit(1);
}
static enum fig_shape decode_shape(const char *s)
{
enum fig_shape res;
res = do_decode_shape(s);
free((void *) s);
return res;
}
/* ----- Text -------------------------------------------------------------- */
static void draw_text(int x, int y, const char *s, int dir, int dim,
enum fig_shape shape)
{
struct text txt = {
.s = s,
.size = dim,
.x = x,
.y = y,
.rot = 0,
.hor = text_min,
.vert = text_min,
};
switch (dir) {
case 0: /* right */
break;
case 1: /* up */
text_rot(&txt, 90);
break;
case 2: /* left */
txt.hor = text_max;
break;
case 3: /* down */
text_rot(&txt, 90);
txt.hor = text_max;
break;
default:
assert(2 + 2 == 5);
}
text_fig(&txt, COLOR_TEXT, LAYER_TEXT);
}
/* ----- Component fields -------------------------------------------------- */
struct sch_field {
struct text txt;
struct sch_field *next;
};
void decode_alignment(struct text *txt, char hor, char vert)
{
switch (hor) {
case 'L':
txt->hor = text_min;
break;
case 'C':
txt->hor = text_mid;
break;
case 'R':
txt->hor = text_max;
break;
default:
assert(0);
}
switch (vert) {
case 'B':
txt->vert = text_min;
break;
case 'C':
txt->vert = text_mid;
break;
case 'T':
txt->vert = text_max;
break;
default:
assert(0);
}
}
static bool parse_field(struct sch_ctx *ctx, const char *line)
{
int n;
unsigned flags;
char hv, hor, vert, italic, bold;
struct sch_field *field;
struct text *txt;
field = alloc_type(struct sch_field);
txt = &field->txt;
if (sscanf(line, "F %d \"\" %c %d %d %u %u %c %c%c%c",
&n, &hv, &txt->x, &txt->y, &txt->size, &flags, &hor, &vert,
&italic, &bold) == 10) {
free(field);
return 1;
}
if (sscanf(line, "F %d \"%m[^\"]\" %c %d %d %u %u %c %c%c%c",
&n, &txt->s, &hv, &txt->x, &txt->y, &txt->size, &flags,
&hor, &vert, &italic, &bold) != 11)
return 0;
if (flags || !lib_field_visible(ctx->comp, n)) {
free(field);
return 1;
}
if (n == 0 && ctx->comp->units > 1) {
int len = strlen(txt->s);
char *s;
s = realloc((void *) txt->s, len + 3);
if (!s) {
perror("realloc");
exit(1);
}
if (ctx->unit <= 26)
sprintf(s + len, "%c", 'A' + ctx->unit - 1);
else
sprintf(s + len, "%c%c",
'A' + (ctx->unit - 1) / 26 - 1,
'A' + (ctx->unit - 1) % 26);
txt->s = s;
}
field->next = ctx->fields;
ctx->fields = field;
switch (hv) {
case 'H':
txt->rot = 0;
break;
case 'V':
txt->rot = 90;
break;
default:
assert(0);
}
decode_alignment(txt, hor, vert);
// @@@ decode font
return 1;
}
static void dump_field(const struct sch_field *field, int m[6])
{
struct text txt = field->txt;
int dx, dy;
dx = txt.x - m[0];
dy = txt.y - m[3];
txt.x = mx(dx, dy, m);
txt.y = my(dx, dy, m);
text_rot(&txt, matrix_to_angle(m));
switch (txt.rot) {
case 180:
text_rot(&txt, 180);
txt.hor = text_flip(txt.hor);
txt.vert = text_flip(txt.vert);
break;
case 270:
text_rot(&txt, 180);
txt.vert = text_flip(txt.vert);
txt.hor = text_flip(txt.hor);
break;
default:
break;
}
if (matrix_is_mirrored(m)) {
if ((txt.rot % 180) == 0)
txt.hor = text_flip(txt.hor);
else
txt.vert = text_flip(txt.vert);
}
text_fig(&txt, COLOR_FIELD, LAYER_FIELD);
}
static void dump_fields(struct sch_field *fields, int m[6])
{
while (fields) {
struct text *txt = &fields->txt;
struct sch_field *next = fields->next;
dump_field(fields, m);
text_free(txt);
free(fields);
fields = next;
}
}
/* ----- Sheet field ------------------------------------------------------- */
static enum fig_shape decode_form(char form)
{
switch (form) {
case 'O':
return fig_in;
case 'I':
return fig_out;
case 'B':
/* fall through */
case 'T':
return fig_bidir;
case 'U':
return fig_unspec;
default:
fprintf(stderr, "unknown form: \"%c\"\n", form);
exit(1);
}
}
static int decode_side(char side)
{
switch (side) {
case 'L':
return 2; /* left */
case 'B':
return 1; /* up */
case 'R':
return 0; /* right */
case 'T':
return 3; /* down */
default:
fprintf(stderr, "unknown side: \"%c\"\n", side);
exit(1);
}
}
static bool parse_hsheet_field(struct sch_ctx *ctx, const char *line)
{
char *s;
int x, y;
char form, side;
unsigned n, dim;
if (sscanf(line, "F%d \"%m[^\"]\" %u", &n, &s, &dim) == 3) {
switch (n) {
case 0:
ctx->sheet = s;
ctx->sheet_dim = dim;
return 1;
case 1:
ctx->file = s;
ctx->file_dim = dim;
return 1;
default:
assert(0);
}
}
if (sscanf(line, "F%d \"%m[^\"]\" %c %c %d %d %u",
&n, &s, &form, &side, &x, &y, &dim) != 7)
return 0;
assert(n >= 2);
if (side == 'B' || side == 'T') {
/*
* This is beautiful: since there is no indication for rotation
* on the hsheet, or the sheet or file fields, we need to look
* at whether the imported sheet pins go left or right (no
* rotation) or whether they go top or bottom (rotation).
*
* A sheet with no imported pins lacks these hints, and is
* therefore always assumed to be without rotation.
*
* Eeschema is careful to be consistent, and does not allow
* sheets with no imported pins to be rotated. Even better, it
* flips rotated sheets where the last imported pin is deleted
* back.
*/
ctx->rotated = 1;
}
dwg_hlabel(x, y, s, decode_side(side), dim, decode_form(form));
free(s);
return 1;
}
static void do_hsheet_text(struct sch_ctx *ctx)
{
char *s;
assert(ctx->sheet && ctx->file);
struct text sheet_txt = {
.size = ctx->sheet_dim,
.x = ctx->x,
.y = ctx->y,
.rot = 0,
.hor = text_min,
.vert = text_min,
};
if (asprintf(&s, "Sheet: %s", ctx->sheet)) {}
sheet_txt.s = s; /* work around "const" mismatch */
struct text file_txt = {
.size = ctx->file_dim,
.x = ctx->x,
.y = ctx->y,
.rot = 0,
.hor = text_min,
.vert = text_max,
};
if (asprintf(&s, "File: %s", ctx->file)) {}
file_txt.s = s; /* work around "const" mismatch */
if (ctx->rotated) {
sheet_txt.rot = file_txt.rot = 90;
sheet_txt.x -= HSHEET_FIELD_OFFSET;
sheet_txt.y += ctx->h;
file_txt.x += ctx->w + HSHEET_FIELD_OFFSET;
file_txt.y += ctx->h;
} else {
sheet_txt.y -= HSHEET_FIELD_OFFSET;
file_txt.y += ctx->h + HSHEET_FIELD_OFFSET;
}
text_fig(&sheet_txt, COLOR_HSHEET_SHEET, LAYER_HSHEET_FIELD);
text_fig(&file_txt, COLOR_HSHEET_FILE, LAYER_HSHEET_FIELD);
free((void *) ctx->sheet);
free((void *) ctx->file);
}
/* ----- Schematics parser ------------------------------------------------- */
bool sch_parse(struct sch_ctx *ctx, const char *line)
{
int n = 0;
int x, y, ex, ey;
char *s;
int m[4];
ctx->lineno++;
switch (ctx->state) {
case sch_basic:
if (sscanf(line, "$Comp%n", &n) == 0 && n) {
ctx->state = sch_comp;
ctx->fields = NULL;
return 1;
}
if (sscanf(line, "$Sheet%n", &n) == 0 && n) {
ctx->state = sch_sheet;
ctx->sheet = NULL;
ctx->file = NULL;
ctx->rotated = 0;
return 1;
}
/* Text */
if (sscanf(line, "Text Notes %d %d %d %d",
&ctx->x, &ctx->y, &ctx->dir, &ctx->dim) == 4) {
ctx->state = sch_text;
ctx->text = draw_text;
return 1;
}
if (sscanf(line, "Text GLabel %d %d %d %d %ms",
&ctx->x, &ctx->y, &ctx->dir, &ctx->dim, &s) == 5) {
ctx->state = sch_text;
ctx->shape = decode_shape(s);
ctx->text = dwg_glabel;
return 1;
}
if (sscanf(line, "Text HLabel %d %d %d %d %ms",
&ctx->x, &ctx->y, &ctx->dir, &ctx->dim, &s) == 5) {
ctx->state = sch_text;
ctx->shape = decode_shape(s);
ctx->text = dwg_hlabel;
return 1;
}
if (sscanf(line, "Text Label %d %d %d %d",
&ctx->x, &ctx->y, &ctx->dir, &ctx->dim) == 4) {
ctx->state = sch_text;
ctx->text = dwg_label;
return 1;
}
/* Connection */
if (sscanf(line, "Connection ~ %d %d", &x, &y) == 2) {
dwg_junction(x, y);
return 1;
}
/* NoConn */
if (sscanf(line, "NoConn ~ %d %d", &x, &y) == 2) {
dwg_noconn(x, y);
return 1;
}
/* Wire */
if (sscanf(line, "Wire Wire Line%n", &n) == 0 && n) {
ctx->state = sch_wire;
ctx->wire = fig_wire;
return 1;
}
if (sscanf(line, "Wire Bus Line%n", &n) == 0 && n) {
ctx->state = sch_wire;
ctx->wire = fig_bus;
return 1;
}
if (sscanf(line, "Wire Notes Line%n", &n) == 0 && n) {
ctx->state = sch_wire;
ctx->wire = fig_line;
return 1;
}
/* Entry */
/*
* Documentation mentions the following additional variants:
*
* - Entry Wire Line equivalent:
* Wire Wire Bus
* Entry Wire Bus
*
* - Entry Bus Bus equivalent:
* Wire Bus Bus
*/
if (sscanf(line, "Entry Wire Line%n", &n) == 0 && n) {
ctx->state = sch_wire;
ctx->wire = fig_wire;
return 1;
}
if (sscanf(line, "Entry Bus Bus%n", &n) == 0 && n) {
ctx->state = sch_wire;
ctx->wire = fig_bus;
return 1;
}
/* EndSCHEMATC */
if (sscanf(line, "$EndSCHEMATC%n", &n) == 0 && n)
return 0;
break;
case sch_descr:
if (sscanf(line, "$EndDescr%n", &n) || !n)
return 1;
ctx->state = sch_basic;
return 1;
case sch_comp:
if (sscanf(line, "$EndComp%n", &n) == 0 && n) {
ctx->state = sch_basic;
ctx->comp = NULL;
return 1;
}
if (sscanf(line, "L %ms", &s) == 1) {
ctx->comp = lib_find(s);
free(s);
return 1;
}
if (sscanf(line, "U %u", &ctx->unit) == 1)
return 1;
if (sscanf(line, "P %d %d", &ctx->x, &ctx->y) == 2)
return 1;
if (parse_field(ctx, line))
return 1;
if (sscanf(line, "AR %n", &n) == 0 && n)
return 1; /* @@@ what is "AR" ? */
n = sscanf(line, " %d %d %d %d", m + 1, m + 2, m + 4, m + 5);
if (n == 3)
return 1;
if (n == 4) {
m[0] = ctx->x;
m[3] = ctx->y;
lib_exec(ctx->comp, ctx->unit, m);
dump_fields(ctx->fields, m);
return 1;
}
break;
case sch_sheet:
if (sscanf(line, "$EndSheet%n", &n) == 0 && n) {
ctx->state = sch_basic;
do_hsheet_text(ctx);
return 1;
}
if (sscanf(line, "S %d %d %u %u",
&ctx->x, &ctx->y, &ctx->w, &ctx->h) == 4) {
fig_rect(ctx->x, ctx->y,
ctx->x + ctx->w, ctx->y + ctx->h,
COLOR_HSHEET_BOX, COLOR_NONE, LAYER_HSHEET_BOX);
return 1;
}
if (sscanf(line, "U %*x%n", &n) == 0 && n)
return 1;
if (parse_hsheet_field(ctx, line))
return 1;
break;
case sch_text:
ctx->state = sch_basic;
if (ctx->text) {
const char *from;
char *to;
s = alloc_size(strlen(line) + 1);
from = line;
to = s;
while (*from) {
if (from[0] != '\\' || from[1] != 'n') {
*to++ = *from++;
continue;
}
*to++ = '\n';
from += 2;
}
*to = 0;
ctx->text(ctx->x, ctx->y, s, ctx->dir, ctx->dim,
ctx->shape);
free(s);
}
return 1;
case sch_wire:
if (sscanf(line, "%d %d %d %d", &x, &y, &ex, &ey) != 4)
break;
if (ctx->wire)
ctx->wire(x, y, ex, ey);
ctx->state = sch_basic;
return 1;
default:
abort();
}
fprintf(stderr, "%u: cannot parse\n\"%s\"\n", ctx->lineno, line);
exit(1);
}
void sch_init(struct sch_ctx *ctx)
{
ctx->state = sch_descr;
ctx->lineno = 0;
}