summaryrefslogtreecommitdiff
path: root/kicad
diff options
context:
space:
mode:
authorWerner Almesberger <werner@almesberger.net>2016-08-17 21:15:44 -0300
committerWerner Almesberger <werner@almesberger.net>2016-08-17 21:15:44 -0300
commit0f5280c70c59d89595b8e2557a996c695ff401ad (patch)
tree8ca71f63a881b426497675ce4f5d38258176714d /kicad
parent265b34fd1c1e5771adae3052b3e459d5eac597c7 (diff)
downloadeeshow-0f5280c70c59d89595b8e2557a996c695ff401ad.tar.gz
eeshow-0f5280c70c59d89595b8e2557a996c695ff401ad.tar.bz2
eeshow-0f5280c70c59d89595b8e2557a996c695ff401ad.zip
eeshow/: move parsing and high-level rendering to kicad/
Diffstat (limited to 'kicad')
-rw-r--r--kicad/dwg.c493
-rw-r--r--kicad/dwg.h51
-rw-r--r--kicad/lib-parse.c358
-rw-r--r--kicad/lib-render.c442
-rw-r--r--kicad/lib.h134
-rw-r--r--kicad/sch-parse.c694
-rw-r--r--kicad/sch-render.c177
-rw-r--r--kicad/sch.h138
8 files changed, 2487 insertions, 0 deletions
diff --git a/kicad/dwg.c b/kicad/dwg.c
new file mode 100644
index 0000000..9943775
--- /dev/null
+++ b/kicad/dwg.c
@@ -0,0 +1,493 @@
+/*
+ * kicad/dwg.c - Complex drawing functions for KiCad items
+ *
+ * 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 <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "util.h"
+#include "misc.h"
+#include "style.h"
+#include "text.h"
+#include "gfx.h"
+#include "kicad/dwg.h"
+
+
+/* ----- Helper functions -------------------------------------------------- */
+
+
+static void bbox_from_poly(struct dwg_bbox *bbox, unsigned n,
+ const int *vx, const int *vy)
+{
+ unsigned i;
+ int xmax, ymax;
+
+ bbox->x = xmax = *vx;
+ bbox->y = ymax = *vy;
+ for (i = 1; i != n; i++) {
+ if (vx[i] < bbox->x)
+ bbox->x = vx[i];
+ if (vy[i] < bbox->y)
+ bbox->y = vy[i];
+ if (vx[i] > xmax)
+ xmax = vx[i];
+ if (vy[i] > ymax)
+ ymax = vy[i];
+ }
+ bbox->w = xmax - bbox->x + 1;
+ bbox->h = ymax - bbox->y + 1;
+}
+
+
+/* ----- Labels ------------------------------------------------------------ */
+
+
+enum box_type { // ___
+ box_simple, // [___]
+ box_left, // <___]
+ box_right, // [___>
+ box_both, // <___>
+};
+
+
+static enum box_type flip_box(enum box_type box)
+{
+ switch (box) {
+ case box_simple:
+ return box_simple;
+ case box_left:
+ return box_right;
+ case box_right:
+ return box_left;
+ case box_both:
+ return box_both;
+ default:
+ abort();
+ }
+}
+
+
+void dwg_label(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox)
+{
+ struct text txt = {
+ .s = s,
+ .size = dim,
+ .x = x,
+ .y = y,
+ .rot = 0,
+ .hor = 0,
+ .vert = text_min,
+ };
+ int dx = 0, dy = 0;
+
+ switch (dir) {
+ case 0: /* right */
+ txt.rot = 0;
+ txt.hor = text_min;
+ dy = 1;
+ break;
+ case 1: /* up */
+ txt.rot = 90;
+ txt.hor = text_min;
+ dx = -1;
+ break;
+ case 2: /* left */
+ txt.rot = 0;
+ txt.hor = text_max;
+ dy = 1;
+ break;
+ case 3: /* down */
+ txt.rot = 90;
+ txt.hor = text_max;
+ dx = -1;
+ break;
+ default:
+ assert(0);
+ }
+
+ txt.y -= dy * LABEL_OFFSET;
+ txt.x += dx * LABEL_OFFSET;
+ text_fig(&txt, COLOR_LABEL, LAYER_LABEL);
+}
+
+
+void dwg_glabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox)
+{
+ struct text txt = {
+ .s = s,
+ .size = dim,
+ .x = x,
+ .y = y,
+ .rot = 0,
+ .hor = 0,
+ .vert = text_mid,
+ };
+ int n = 6;
+ int vx[7];
+ int vy[7];
+ int half = (dim >> 1) + GLABEL_OFFSET;
+ enum box_type box;
+ int dx, shift_flat, shift_tip;
+ bool anchor_right = 1;
+ char *tag;
+
+ switch (shape) {
+ case dwg_unspec:
+ box = box_simple;
+ break;
+ case dwg_in:
+ box = box_right;
+ break;
+ case dwg_out:
+ box = box_left;
+ break;
+ case dwg_bidir:
+ box = box_both;
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (dir) {
+ case 0: /* left */
+ txt.rot = 0;
+ txt.hor = text_max;
+ dx = -1;
+ break;
+ case 1: /* up */
+ txt.rot = 90;
+ txt.hor = text_min;
+ dx = 1;
+ box = flip_box(box);
+ anchor_right = !anchor_right;
+ break;
+ case 2: /* right */
+ txt.rot = 0;
+ txt.hor = text_min;
+ dx = 1;
+ box = flip_box(box);
+ anchor_right = !anchor_right;
+ break;
+ case 3: /* down */
+ txt.rot = 90;
+ txt.hor = text_max;
+ dx = -1;
+ break;
+ default:
+ assert(0);
+ }
+
+ shift_flat = dx * GLABEL_OFFSET;
+ shift_tip = dx * (GLABEL_OFFSET + half);
+
+ switch (box) {
+ case box_simple:
+ n = 5;
+ text_shift(&txt, txt.hor, text_mid, shift_flat, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ break;
+ case box_right:
+ text_shift(&txt, txt.hor, text_mid,
+ anchor_right ? shift_tip : shift_flat, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0,
+ vx + 3, vy + 3);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5);
+ break;
+ case box_left:
+ text_shift(&txt, txt.hor, text_mid,
+ anchor_right ? shift_flat : shift_tip, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0,
+ vx + 5, vy + 5);
+ break;
+ case box_both:
+ n = 7;
+ text_shift(&txt, txt.hor, text_mid, shift_tip, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0,
+ vx + 3, vy + 3);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5);
+ text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0,
+ vx + 6, vy + 6);
+ break;
+ default:
+ assert(0);
+ }
+
+ text_fig(&txt, COLOR_GLABEL, LAYER_GLABEL);
+
+ vx[0] = vx[n - 1];
+ vy[0] = vy[n - 1];
+ gfx_poly(n, vx, vy, COLOR_GLABEL, COLOR_NONE, LAYER_GLABEL);
+
+ if (bbox)
+ bbox_from_poly(bbox, n, vx, vy);
+
+ if (asprintf(&tag, "G:%s", s)) {}
+ gfx_tag(tag, n, vx, vy);
+}
+
+
+static int make_box(enum box_type box, int h, int *vx, int *vy)
+{
+ int r = h / 2;
+
+ switch (box) {
+ case box_simple:
+ vx[0] = 0;
+ vy[0] = -r;
+ vx[1] = 2 * r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = r;
+ vx[3] = 0;
+ vy[3] = r;
+ return 4;
+ case box_right:
+ vx[0] = 0;
+ vy[0] = -r;
+ vx[1] = r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = 0;
+ vx[3] = r;
+ vy[3] = r;
+ vx[4] = 0;
+ vy[4] = r;
+ return 5;
+ case box_left:
+ vx[0] = r;
+ vy[0] = -r;
+ vx[1] = 2 * r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = r;
+ vx[3] = r;
+ vy[3] = r;
+ vx[4] = 0;
+ vy[4] = 0;
+ return 5;
+ case box_both:
+ vx[0] = 0;
+ vy[0] = 0;
+ vx[1] = r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = 0;
+ vx[3] = r;
+ vy[3] = r;
+ return 4;
+ default:
+ assert(0);
+ }
+}
+
+
+void dwg_hlabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox)
+{
+ struct text txt = {
+ .s = s,
+ .size = dim,
+ .x = x,
+ .y = y,
+ .rot = 0,
+ .hor = 0,
+ .vert = text_mid,
+ };
+ int vx[6], vy[6];
+ int rot;
+ int n, i;
+
+ switch (shape) {
+ case dwg_unspec:
+ n = make_box(box_simple, dim, vx, vy);
+ break;
+ case dwg_in:
+ n = make_box(box_left, dim, vx, vy);
+ break;
+ case dwg_out:
+ n = make_box(box_right, dim, vx, vy);
+ break;
+ case dwg_tri:
+ case dwg_bidir:
+ n = make_box(box_both, dim, vx, vy);
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (dir) {
+ case 0: /* right */
+ rot = 180;
+ txt.hor = text_max;
+ break;
+ case 1: /* up */
+ rot = 90;
+ txt.hor = text_min;
+ break;
+ case 2: /* left */
+ rot = 0;
+ txt.hor = text_min;
+ break;
+ case 3: /* down */
+ rot = 270;
+ txt.hor = text_max;
+ break;
+ default:
+ assert(0);
+ }
+
+ txt.x += rx((1 + HLABEL_OFFSET_F) * dim, 0, rot);
+ txt.y += ry((1 + HLABEL_OFFSET_F) * dim, 0, rot);
+
+ for (i = 0; i != n; i++) {
+ int tmp;
+
+ tmp = x + rx(vx[i], vy[i], rot);
+ vy[i] = y + ry(vx[i], vy[i], rot);
+ vx[i] = tmp;
+ }
+
+ vx[n] = vx[0];
+ vy[n] = vy[0];
+
+ txt.rot = rot % 180;
+
+ text_fig(&txt, COLOR_HLABEL, LAYER_HLABEL);
+ gfx_poly(n + 1, vx, vy, COLOR_HLABEL, COLOR_NONE, LAYER_HLABEL);
+}
+
+
+/* ----- Text -------------------------------------------------------------- */
+
+
+void dwg_text(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox)
+{
+ 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);
+}
+
+
+/* ----- Connections ------------------------------------------------------- */
+
+
+void dwg_junction(int x, int y)
+{
+ gfx_circ(x, y, JUNCTION_R, COLOR_NONE, COLOR_WIRE, LAYER_WIRES);
+}
+
+
+void dwg_noconn(int x, int y)
+{
+ int vx[2] = { x - NOCONN_LEN, x + NOCONN_LEN };
+ int vy[2] = { y - NOCONN_LEN, y + NOCONN_LEN };
+
+ gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN);
+ swap(vy[0], vy[1]);
+ gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN);
+}
+
+
+/* ----- Lines ------------------------------------------------------------- */
+
+/*
+ * We can't use gfx_poly because lines are dashed and we don't have that
+ * property at the gfx_poly API.
+ */
+
+void dwg_line(int sx, int sy, int ex, int ey)
+{
+ gfx_line(sx, sy, ex, ey, COLOR_SHEET_DWG, LAYER_LINES);
+}
+
+
+/* ----- Wires and busses -------------------------------------------------- */
+
+
+void dwg_wire(int sx, int sy, int ex, int ey)
+{
+ int vx[] = { sx, ex };
+ int vy[] = { sy, ey };
+
+ // WIDTH_WIRE
+ gfx_poly(2, vx, vy, COLOR_WIRE, COLOR_NONE, LAYER_WIRES);
+}
+
+
+void dwg_bus(int sx, int sy, int ex, int ey)
+{
+ int vx[] = { sx, ex };
+ int vy[] = { sy, ey };
+
+ // WIDTH_BUS
+ gfx_poly(2, vx, vy, COLOR_BUS, COLOR_NONE, LAYER_BUSSES);
+}
diff --git a/kicad/dwg.h b/kicad/dwg.h
new file mode 100644
index 0000000..d4039a7
--- /dev/null
+++ b/kicad/dwg.h
@@ -0,0 +1,51 @@
+/*
+ * kicad/dwg.h - Complex drawing functions for KiCad items
+ *
+ * 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.
+ */
+
+
+#ifndef KICAD_DWG_H
+#define KICAD_DWG_H
+
+#include "fig.h"
+
+
+enum dwg_shape {
+ dwg_unspec, // UnSpc
+ dwg_in, // Input
+ dwg_out, // Output
+ dwg_tri, // 3State
+ dwg_bidir, // Bidirectional
+};
+
+struct dwg_bbox {
+ int x, y;
+ int w, h;
+};
+
+
+void dwg_label(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox);
+void dwg_hlabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox);
+void dwg_glabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox);
+void dwg_text(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape, struct dwg_bbox *bbox);
+
+void dwg_junction(int x, int y);
+void dwg_noconn(int x, int y);
+
+void dwg_line(int sx, int sy, int ex, int ey);
+
+void dwg_wire(int sx, int sy, int ex, int ey);
+void dwg_bus(int sx, int sy, int ex, int ey);
+
+#endif /* !KICAD_DWG_H */
diff --git a/kicad/lib-parse.c b/kicad/lib-parse.c
new file mode 100644
index 0000000..a4c8d6f
--- /dev/null
+++ b/kicad/lib-parse.c
@@ -0,0 +1,358 @@
+/*
+ * kicad/lib.c - Parse Eeschema .lib 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "util.h"
+#include "diag.h"
+#include "text.h"
+#include "file/file.h"
+#include "kicad/lib.h"
+
+
+/* ----- Text -------------------------------------------------------------- */
+
+
+static enum text_style decode_style(const char *s)
+{
+ if (!strcmp(s, "Normal"))
+ return text_normal;
+ if (!strcmp(s, "Italic"))
+ return text_italic;
+ assert(0);
+}
+
+
+/* ----- Polygons ---------------------------------------------------------- */
+
+
+static bool parse_poly(struct lib_poly *poly, const char *line, int points)
+{
+ int i, n;
+
+ poly->points = points;
+ poly->x = alloc_type_n(int, points);
+ poly->y = alloc_type_n(int, points);
+ for (i = 0; i != points; i++) {
+ if (sscanf(line, "%d %d %n",
+ poly->x + i, poly->y + i, &n) != 2)
+ return 0;
+ line += n;
+ }
+ if (sscanf(line, "%c", &poly->fill) != 1)
+ return 0;
+ return 1;
+}
+
+
+/* ----- Definitions ------------------------------------------------------- */
+
+
+static bool parse_def(struct lib *lib, const char *line)
+{
+ char *s, *ref;
+ char draw_num, draw_name;
+ unsigned name_offset;
+ unsigned units;
+ bool power;
+
+ if (sscanf(line, "DEF %ms %ms %*d %u %c %c %u",
+ &s, &ref, &name_offset, &draw_num, &draw_name, &units) != 6)
+ return 0;
+
+ power = *ref == '#';
+ free(ref);
+
+ lib->curr_comp = alloc_type(struct comp);
+ if (*s == '~') {
+ char *tmp = alloc_size(strlen(s) + 1);
+
+ /* we can't just s++, since that would break freeing */
+ strcpy(tmp, s + 1);
+ free(s);
+ s = tmp;
+ }
+ lib->curr_comp->name = s;
+ lib->curr_comp->aliases = NULL;
+ lib->curr_comp->units = units;
+
+ lib->curr_comp->visible = 0;
+ lib->curr_comp->show_pin_name = draw_name == 'Y' && !power;
+ lib->curr_comp->show_pin_num = draw_num == 'Y' && !power;
+ lib->curr_comp->name_offset = name_offset;
+
+ lib->curr_comp->objs = NULL;
+ lib->next_obj = &lib->curr_comp->objs;
+
+ lib->curr_comp->next = NULL;
+ *lib->next_comp = lib->curr_comp;
+ lib->next_comp = &lib->curr_comp->next;
+
+ return 1;
+}
+
+
+/* ----- Arcs -------------------------------------------------------------- */
+
+
+static bool parse_arc(struct lib_obj *obj, const char *line)
+{
+ struct lib_arc *arc = &obj->u.arc;
+ int a1, a2;
+
+ if (sscanf(line, "A %d %d %d %d %d %u %u %u %c",
+ &arc->x, &arc->y, &arc->r, &a1, &a2, &obj->unit, &obj->convert,
+ &arc->thick, &arc->fill) != 9)
+ return 0;
+
+ /*
+ * KiCad arcs can be clockwise or counter-clockwise. They must always
+ * be smaller than 180 degrees.
+ */
+
+ while (a1 < 0)
+ a1 += 3600;
+ while (a2 < 0)
+ a2 += 3600;
+ a1 %= 3600;
+ a2 %= 3600;
+ if (a2 < a1)
+ a2 += 3600;
+ assert(a2 - a1 != 1800);
+ if (a2 - a1 > 1800)
+ swap(a1, a2);
+
+ arc->start_a = (a1 % 3600) / 10;
+ arc->end_a = (a2 % 3600) / 10;
+
+ return 1;
+}
+
+
+/* ----- Aliases ----------------------------------------------------------- */
+
+
+static void add_alias(struct comp *comp, const char *alias)
+{
+ struct comp_alias *new;
+
+ new = alloc_type(struct comp_alias);
+ new->name = alias;
+ new->next = comp->aliases;
+ comp->aliases = new;
+}
+
+
+/* ----- Library parser ---------------------------------------------------- */
+
+
+static bool lib_parse_line(const struct file *file,
+ void *user, const char *line)
+{
+ struct lib *lib = user;
+ int n = 0;
+ unsigned points;
+ struct lib_obj *obj;
+ char *s, *style;
+ unsigned zero1, zero2;
+ char vis;
+
+ switch (lib->state) {
+ case lib_skip:
+ if (parse_def(lib, line)) {
+ lib->state = lib_def;
+ return 1;
+ }
+ return 1;
+ case lib_def:
+ if (sscanf(line, "DRAW%n", &n) == 0 && n) {
+ lib->state = lib_draw;
+ return 1;
+ }
+ if (sscanf(line, "F%d \"\" %*d %*d %*d %*c %c", &n, &vis) == 2
+ || sscanf(line, "F%d \"%*[^\"]\" %*d %*d %*d %*c %c",
+ &n, &vis) == 2) {
+ if (vis == 'V')
+ lib->curr_comp->visible |= 1 << n;
+ return 1;
+ }
+ if (sscanf(line, "ALIAS %ms", &s) == 1) {
+ add_alias(lib->curr_comp, s);
+ return 1;
+ }
+ /* @@@ explicitly ignore FPLIST */
+ return 1;
+ case lib_draw:
+ if (sscanf(line, "ENDDRAW%n", &n) == 0 && n) {
+ lib->state = lib_skip;
+ return 1;
+ }
+
+ obj = alloc_type(struct lib_obj);
+ obj->next = NULL;
+ *lib->next_obj = obj;
+ lib->next_obj = &obj->next;
+
+ if (sscanf(line, "P %u %u %u %u %n",
+ &points, &obj->unit, &obj->convert, &obj->u.poly.thick,
+ &n) == 4) {
+ obj->type = lib_obj_poly;
+ if (parse_poly(&obj->u.poly, line + n, points))
+ return 1;
+ break;
+ }
+ if (sscanf(line, "S %d %d %d %d %u %u %d %c",
+ &obj->u.rect.sx, &obj->u.rect.sy, &obj->u.rect.ex,
+ &obj->u.rect.ey, &obj->unit, &obj->convert,
+ &obj->u.rect.thick, &obj->u.rect.fill) == 8) {
+ obj->type = lib_obj_rect;
+ return 1;
+ }
+ if (sscanf(line, "C %d %d %d %u %u %d %c",
+ &obj->u.circ.x, &obj->u.circ.y, &obj->u.circ.r,
+ &obj->unit, &obj->convert, &obj->u.circ.thick,
+ &obj->u.circ.fill) == 7) {
+ obj->type = lib_obj_circ;
+ return 1;
+ }
+ if (parse_arc(obj, line)) {
+ obj->type = lib_obj_arc;
+ return 1;
+ }
+ n = sscanf(line,
+ "T %d %d %d %d %u %u %u \"%m[^\"]\" %ms %u %c %c",
+ &obj->u.text.orient, &obj->u.text.x, &obj->u.text.y,
+ &obj->u.text.dim, &zero1, &obj->unit, &obj->convert,
+ &obj->u.text.s, &style, &zero2,
+ &obj->u.text.hor_align, &obj->u.text.vert_align);
+ if (n != 12) {
+ n = sscanf(line,
+ "T %d %d %d %d %u %u %u %ms %ms %u %c %c",
+ &obj->u.text.orient, &obj->u.text.x, &obj->u.text.y,
+ &obj->u.text.dim, &zero1, &obj->unit, &obj->convert,
+ &obj->u.text.s, &style, &zero2,
+ &obj->u.text.hor_align, &obj->u.text.vert_align);
+ while (n == 12) {
+ char *tilde;
+
+ tilde = strchr(obj->u.text.s, '~');
+ if (!tilde)
+ break;
+ *tilde = ' ';
+ }
+ }
+ /*
+ * zero2 seems to be the font style: 0 = normal, 1 = bold ?
+ */
+ if (n == 12) {
+ if (zero1)
+ fatal("%u: only understand 0 x x\n"
+ "\"%s\"\n", file->lineno, line);
+ obj->u.text.style = decode_style(style);
+ obj->type = lib_obj_text;
+ return 1;
+ }
+ if (sscanf(line, "X %ms %ms %d %d %d %c %d %d %u %u %c",
+ &obj->u.pin.name, &obj->u.pin.number,
+ &obj->u.pin.x, &obj->u.pin.y, &obj->u.pin.length,
+ &obj->u.pin.orient,
+ &obj->u.pin.number_size, &obj->u.pin.name_size,
+ &obj->unit, &obj->convert, &obj->u.pin.etype) == 11) {
+ obj->type = lib_obj_pin;
+ return 1;
+ }
+ break;
+ default:
+ abort();
+ }
+ fatal("%u: cannot parse\n\"%s\"\n", file->lineno, line);
+}
+
+
+bool lib_parse_file(struct lib *lib, struct file *file)
+{
+ lib->state = lib_skip;
+ return file_read(file, lib_parse_line, lib);
+}
+
+
+
+bool lib_parse(struct lib *lib, const char *name, const struct file *related)
+{
+ struct file file;
+ bool res;
+
+ if (!file_open(&file, name, related))
+ return 0;
+ res = lib_parse_file(lib, &file);
+ file_close(&file);
+ return res;
+}
+
+
+void lib_init(struct lib *lib)
+{
+ lib->comps = NULL;
+ lib->next_comp = &lib->comps;
+}
+
+
+static void free_objs(struct lib_obj *objs)
+{
+ struct lib_obj *next;
+
+ while (objs) {
+ next = objs->next;
+ switch (objs->type) {
+ case lib_obj_text:
+ free((char *) objs->u.text.s);
+ break;
+ case lib_obj_pin:
+ free((char *) objs->u.pin.name);
+ free((char *) objs->u.pin.number);
+ break;
+ default:
+ break;
+ }
+ free(objs);
+ objs = next;
+ }
+}
+
+
+static void free_comp(struct comp *comp)
+{
+ struct comp_alias *next;
+
+ free((char *) comp->name);
+ while (comp->aliases) {
+ next = comp->aliases->next;
+ free((char *) comp->aliases->name);
+ comp->aliases = next;
+ }
+ free_objs(comp->objs);
+ free(comp);
+}
+
+
+void lib_free(struct lib *lib)
+{
+ struct comp *comp, *next;
+
+ for (comp = lib->comps; comp; comp = next) {
+ next = comp->next;
+ free_comp(comp);
+ }
+}
diff --git a/kicad/lib-render.c b/kicad/lib-render.c
new file mode 100644
index 0000000..079d776
--- /dev/null
+++ b/kicad/lib-render.c
@@ -0,0 +1,442 @@
+/*
+ * kicad/lib-render.c - Render component from library
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "util.h"
+#include "diag.h"
+#include "misc.h"
+#include "style.h"
+#include "gfx.h"
+#include "text.h"
+#include "kicad/sch.h"
+#include "kicad/lib.h"
+
+
+/* ----- Drawing ----------------------------------------------------------- */
+
+
+static void draw_poly(const struct lib_poly *poly, const int m[6])
+{
+ int n = poly->points;
+ int x[n];
+ int y[n];
+ int i;
+
+ for (i = 0; i != n; i++) {
+ x[i] = mx(poly->x[i], poly->y[i], m);
+ y[i] = my(poly->x[i], poly->y[i], m);
+ }
+
+ gfx_poly(n, x, y, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG);
+
+ switch (poly->fill) {
+ case 'N':
+ break;
+ case 'F':
+ gfx_poly(n, x, y, COLOR_NONE, COLOR_COMP_DWG,
+ LAYER_COMP_DWG_BG);
+ break;
+ case 'f':
+ gfx_poly(n, x, y, COLOR_NONE, COLOR_COMP_DWG_BG,
+ LAYER_COMP_DWG_BG);
+ break;
+ default:
+ abort();
+ }
+}
+
+
+static void draw_rect(const struct lib_rect *rect, const int m[6])
+{
+ int sx = mx(rect->sx, rect->sy, m);
+ int sy = my(rect->sx, rect->sy, m);
+ int ex = mx(rect->ex, rect->ey, m);
+ int ey = my(rect->ex, rect->ey, m);
+
+ gfx_rect(sx, sy, ex, ey, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG);
+
+ switch (rect->fill) {
+ case 'N':
+ break;
+ case 'F':
+ gfx_rect(sx, sy, ex, ey, COLOR_NONE, COLOR_COMP_DWG,
+ LAYER_COMP_DWG_BG);
+ break;
+ case 'f':
+ gfx_rect(sx, sy, ex, ey, COLOR_NONE, COLOR_COMP_DWG_BG,
+ LAYER_COMP_DWG_BG);
+ break;
+ default:
+ abort();
+ }
+}
+
+
+static void draw_circ(const struct lib_circ *circ, const int m[6])
+{
+ int x = mx(circ->x, circ->y, m);
+ int y = my(circ->x, circ->y, m);
+ int r = circ->r;
+
+ gfx_circ(x, y, r, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG);
+
+ switch (circ->fill) {
+ case 'N':
+ break;
+ case 'F':
+ gfx_circ(x, y, r, COLOR_NONE, COLOR_COMP_DWG,
+ LAYER_COMP_DWG_BG);
+ break;
+ case 'f':
+ gfx_circ(x, y, r, COLOR_NONE, COLOR_COMP_DWG_BG,
+ LAYER_COMP_DWG_BG);
+ break;
+ default:
+ abort();
+ }
+}
+
+
+static void draw_arc(const struct lib_arc *arc, const int m[6])
+{
+ int a = matrix_to_angle(m);
+ int x = mx(arc->x, arc->y, m);
+ int y = my(arc->x, arc->y, m);
+ int sa = angle_add(arc->start_a, a);
+ int ea = angle_add(arc->end_a, a);
+
+ if (matrix_is_mirrored(m)) {
+ sa = 180 - sa;
+ ea = 180 - ea;
+ while (ea < sa)
+ ea += 360;
+ while (ea - sa > 360)
+ ea -= 360;
+ if (ea - sa >= 180) {
+ swap(sa, ea);
+ sa += 360;
+ }
+ }
+
+ /*
+ * cr_arc (and maybe others) close the arc if filling, so we supply a
+ * foreground color as well. Other objects are closed and don't need
+ * need a foreground color when filling.
+ */
+ switch (arc->fill) {
+ case 'N':
+ break;
+ case 'F':
+ gfx_arc(x, y, arc->r, sa, ea,
+ COLOR_COMP_DWG, COLOR_COMP_DWG, LAYER_COMP_DWG_BG);
+ break;
+ case 'f':
+ gfx_arc(x, y, arc->r, sa, ea,
+ COLOR_COMP_DWG_BG, COLOR_COMP_DWG_BG, LAYER_COMP_DWG_BG);
+ break;
+ default:
+ assert(0);
+ }
+
+ gfx_arc(x, y, arc->r, sa, ea,
+ COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG);
+}
+
+
+static void draw_pin_name(const struct comp *comp, const struct lib_pin *pin,
+ const int m[6], int dx, int dy, int rot, enum text_align hor)
+{
+ int ox, oy, sx, sy;
+
+ if (!strcmp(pin->name, "~"))
+ return;
+
+ if (comp->name_offset) {
+ ox = dx * (pin->length + comp->name_offset);
+ oy = dy * (pin->length + comp->name_offset);
+ sx = sy = 0;
+ } else {
+ ox = dx * pin->length / 2;
+ oy = dy * pin->length / 2;
+ sx = mxr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m);
+ sy = myr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m);
+ if (sx > 0)
+ sx = -sx;
+ if (sy > 0)
+ sy = -sy;
+ }
+
+ struct text txt = {
+ .s = pin->name,
+ .x = mx(pin->x + ox, pin->y + oy, m) + sx,
+ .y = my(pin->x + ox, pin->y + oy, m) + sy,
+ .size = pin->name_size,
+ .rot = rot,
+ .hor = comp->name_offset ? hor : text_mid,
+ .vert = comp->name_offset ? text_mid : text_min,
+ };
+
+ text_rot(&txt, matrix_to_angle(m));
+ if (matrix_is_mirrored(m)) {
+ if ((txt.rot % 180) == 0)
+ txt.hor = text_flip(txt.hor);
+ else
+ txt.vert = text_flip(txt.vert);
+ }
+
+ switch (txt.rot) {
+ case 180:
+ case 270:
+ text_flip_x(&txt);
+ break;
+ default:
+ break;
+ }
+
+ text_fig(&txt, COLOR_PIN_NAME, LAYER_PIN_NAME);
+}
+
+
+static void draw_pin_num(const struct comp *comp, const struct lib_pin *pin,
+ const int m[6], int dx, int dy, int rot, enum text_align hor)
+{
+ int ox, oy, sx, sy;
+
+ ox = dx * pin->length / 2;
+ oy = dy * pin->length / 2;
+
+ sx = mxr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m);
+ sy = myr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m);
+ if (sx > 0)
+ sx = -sx;
+ if (sy > 0)
+ sy = -sy;
+
+ if (!comp->name_offset) {
+ sx = -sx;
+ sy = -sy;
+ }
+
+ struct text txt = {
+ .s = pin->number,
+ .x = mx(pin->x + ox, pin->y + oy, m) + sx,
+ .y = my(pin->x + ox, pin->y + oy, m) + sy,
+ .size = pin->number_size,
+ .rot = rot,
+ .hor = text_mid,
+ .vert = comp->name_offset ? text_min : text_max,
+ };
+
+ text_rot(&txt, matrix_to_angle(m) % 180);
+ if (matrix_is_mirrored(m)) {
+ switch (txt.rot) {
+ case 0:
+ txt.hor = text_flip(txt.hor);
+ break;
+ case 90:
+ break;
+ case 180:
+ txt.hor = text_flip(txt.hor);
+ break;
+ case 270:
+ break;
+ }
+ }
+
+ switch (txt.rot) {
+ case 180:
+ case 270:
+ text_flip_x(&txt);
+ break;
+ default:
+ break;
+ }
+
+ text_fig(&txt, COLOR_PIN_NUMBER, LAYER_PIN_NUMBER);
+}
+
+
+static void draw_pin(const struct comp *comp, const struct lib_pin *pin,
+ const int m[6])
+{
+ int x[2], y[2];
+ int dx = 0, dy = 0;
+ int rot;
+ enum text_align hor;
+
+ switch (pin->orient) {
+ case 'U':
+ dy = 1;
+ rot = 90;
+ hor = text_min;
+ break;
+ case 'D':
+ dy = -1;
+ rot = 90;
+ hor = text_max;
+ break;
+ case 'R':
+ dx = 1;
+ rot = 0;
+ hor = text_min;
+ break;
+ case 'L':
+ dx = -1;
+ rot = 0;
+ hor = text_max;
+ break;
+ default:
+ abort();
+ }
+ x[0] = mx(pin->x, pin->y, m);
+ y[0] = my(pin->x, pin->y, m);
+ x[1] = mx(pin->x + dx * pin->length, pin->y + dy * pin->length, m);
+ y[1] = my(pin->x + dx * pin->length, pin->y + dy * pin->length, m);
+ gfx_poly(2, x, y, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG);
+
+ if (comp->show_pin_name)
+ draw_pin_name(comp, pin, m, dx, dy, rot, hor);
+
+ if (comp->show_pin_num)
+ draw_pin_num(comp, pin, m, dx, dy, rot, hor);
+}
+
+
+static void draw_text(const struct lib_text *text, const int m[6])
+{
+ struct text txt = {
+ .s = text->s,
+ .size = text->dim,
+ .x = mx(text->x, text->y, m),
+ .y = my(text->x, text->y, m),
+ .rot = angle_add(text->orient / 10, matrix_to_angle(m)),
+ };
+
+ decode_alignment(&txt, text->hor_align, text->vert_align);
+
+ switch (txt.rot) {
+ case 180:
+ case 270:
+ /* @@@ consolidate this with text_flip_x */
+ txt.rot = angle_add(txt.rot, 180);
+ txt.hor = text_flip(txt.hor);
+ txt.vert = text_flip(txt.vert);
+// text_flip_x(&txt);
+ break;
+ default:
+ break;
+ }
+
+ if (matrix_is_mirrored(m))
+ switch (txt.rot) {
+ case 0:
+ case 180:
+ txt.hor = text_flip(txt.hor);
+ break;
+ case 90:
+ case 270:
+ txt.vert = text_flip(txt.vert);
+ break;
+ default:
+ abort();
+ }
+
+ text_fig(&txt, COLOR_COMP_DWG, WIDTH_COMP_DWG);
+}
+
+
+static void draw(const struct comp *comp, const struct lib_obj *obj,
+ const int m[6])
+{
+ switch (obj->type) {
+ case lib_obj_poly:
+ draw_poly(&obj->u.poly, m);
+ break;
+ case lib_obj_rect:
+ draw_rect(&obj->u.rect, m);
+ break;
+ case lib_obj_circ:
+ draw_circ(&obj->u.circ, m);
+ break;
+ case lib_obj_arc:
+ draw_arc(&obj->u.arc, m);
+ break;
+ case lib_obj_text:
+ draw_text(&obj->u.text, m);
+ break;
+ case lib_obj_pin:
+ draw_pin(comp, &obj->u.pin, m);
+ break;
+ default:
+ abort();
+ }
+}
+
+
+const struct comp *lib_find(const struct lib *lib, const char *name)
+{
+ const struct comp *comp;
+ const struct comp_alias *alias;
+
+ for (comp = lib->comps; comp; comp = comp->next) {
+ if (!strcmp(comp->name, name))
+ return comp;
+ for (alias = comp->aliases; alias; alias = alias->next)
+ if (!strcmp(alias->name, name))
+ return comp;
+ }
+ error("\"%s\" not found\n", name);
+ return NULL;
+}
+
+
+bool lib_field_visible(const struct comp *comp, int n)
+{
+ return (comp->visible >> n) & 1;
+}
+
+
+static void missing_component(const int m[4])
+{
+ int sx = mx(0, 0, m);
+ int sy = my(0, 0, m);
+ int ex = mx(MISSING_WIDTH, MISSING_HEIGHT, m);
+ int ey = my(MISSING_WIDTH, MISSING_HEIGHT, m);
+
+ gfx_rect(sx, sy, ex, ey, COLOR_MISSING_FG, COLOR_MISSING_BG,
+ LAYER_COMP_DWG);
+}
+
+
+void lib_render(const struct comp *comp, unsigned unit, unsigned convert,
+ const int m[4])
+{
+ const struct lib_obj *obj;
+
+ if (!comp) {
+ missing_component(m);
+ return;
+ }
+ if (!unit)
+ unit = 1;
+ for (obj = comp->objs; obj; obj = obj->next) {
+ if (obj->unit && obj->unit != unit)
+ continue;
+ if (obj->convert && obj->convert != convert)
+ continue;
+ draw(comp, obj, m);
+ }
+}
diff --git a/kicad/lib.h b/kicad/lib.h
new file mode 100644
index 0000000..f0d418c
--- /dev/null
+++ b/kicad/lib.h
@@ -0,0 +1,134 @@
+/*
+ * kicad/lib.h - Parse Eeschema .lib 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.
+ */
+
+
+#ifndef KICAD_LIB_H
+#define KICAD_LIB_H
+
+#include <stdbool.h>
+
+#include "file/file.h"
+
+
+enum lib_state {
+ lib_skip, /* before a definition */
+ lib_def, /* in definition */
+ lib_draw, /* in drawings */
+};
+
+struct lib_obj {
+ enum lib_obj_type {
+ lib_obj_poly,
+ lib_obj_rect,
+ lib_obj_circ,
+ lib_obj_arc,
+ lib_obj_text,
+ lib_obj_pin,
+ } type;
+ unsigned unit;
+ unsigned convert;
+ union {
+ struct lib_poly {
+ int thick;
+ char fill;
+ int points;
+ int *x;
+ int *y;
+ } poly;
+ struct lib_rect {
+ int thick;
+ char fill;
+ int sx, sy;
+ int ex, ey;
+ } rect;
+ struct lib_circ {
+ int x, y;
+ int r;
+ int thick;
+ char fill;
+ } circ;
+ struct lib_arc {
+ int x, y;
+ int r;
+ int start_a, end_a;
+ int thick;
+ char fill;
+ } arc;
+ struct lib_text {
+ int orient;
+ int x, y;
+ int dim;
+ char *s;
+ enum text_style style;
+ char hor_align;
+ char vert_align;
+ } text;
+ struct lib_pin {
+ char *name;
+ char *number;
+ int x, y;
+ int length;
+ char orient;
+ int number_size;
+ int name_size;
+ char etype;
+ // @@@ shape
+ } pin;
+ } u;
+ struct lib_obj *next;
+};
+
+struct comp_alias {
+ const char *name;
+ struct comp_alias *next;
+};
+
+struct comp {
+ const char *name;
+ struct comp_alias *aliases;
+ unsigned units;
+
+ unsigned visible; /* visible fields, bit mask */
+ bool show_pin_name;
+ bool show_pin_num;
+ unsigned name_offset;
+
+ struct lib_obj *objs;
+ struct comp *next;
+};
+
+struct lib {
+ enum lib_state state;
+
+ struct comp *comps;
+
+ struct comp *curr_comp; /* current component */
+ struct comp **next_comp;
+ struct lib_obj **next_obj;
+
+};
+
+
+extern struct comp *comps;
+
+
+const struct comp *lib_find(const struct lib *lib, const char *name);
+bool lib_field_visible(const struct comp *comp, int n);
+void lib_render(const struct comp *comp, unsigned unit, unsigned convert,
+ const int m[6]);
+
+bool lib_parse_file(struct lib *lib, struct file *file);
+bool lib_parse(struct lib *lib, const char *name, const struct file *related);
+void lib_init(struct lib *lib);
+void lib_free(struct lib *lib);
+
+#endif /* !KICAD_LIB_H */
diff --git a/kicad/sch-parse.c b/kicad/sch-parse.c
new file mode 100644
index 0000000..c4b893f
--- /dev/null
+++ b/kicad/sch-parse.c
@@ -0,0 +1,694 @@
+/*
+ * kicad/sch-parse.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.
+ */
+
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "util.h"
+#include "diag.h"
+#include "kicad/dwg.h"
+#include "file/file.h"
+#include "kicad/lib.h"
+#include "kicad/sch.h"
+
+
+/* ----- (Global) Labels --------------------------------------------------- */
+
+
+static enum dwg_shape do_decode_shape(const char *s)
+{
+ if (!strcmp(s, "UnSpc"))
+ return dwg_unspec;
+ if (!strcmp(s, "Input"))
+ return dwg_in;
+ if (!strcmp(s, "Output"))
+ return dwg_out;
+ if (!strcmp(s, "3State"))
+ return dwg_tri;
+ if (!strcmp(s, "BiDi"))
+ return dwg_bidir;
+ fatal("unknown shape: \"%s\"\n", s);
+}
+
+
+static enum dwg_shape decode_shape(const char *s)
+{
+ enum dwg_shape res;
+
+ res = do_decode_shape(s);
+ free((void *) s);
+ return res;
+}
+
+
+/* ----- Component fields -------------------------------------------------- */
+
+
+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)
+{
+ struct sch_comp *comp = &ctx->obj.u.comp;
+ int n;
+ unsigned flags;
+ char hv, hor, vert, italic, bold;
+ struct comp_field *field;
+ struct text *txt;
+ char *s;
+ const char *p;
+ int pos, len;
+
+ field = alloc_type(struct comp_field);
+ txt = &field->txt;
+ txt->s = NULL;
+
+ 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 \"%n", &n, &pos) != 1)
+ return 0;
+ for (p = line + pos; *p && *p != '"'; p++)
+ if (*p == '\\' && p[1])
+ p++;
+ if (*p != '"')
+ return 0;
+
+ len = p - (line + pos);
+ s = alloc_size(len + 1);
+ memcpy(s, line + pos, len);
+ s[len] = 0;
+
+ if (sscanf(p + 1, " %c %d %d %u %u %c %c%c%c",
+ &hv, &txt->x, &txt->y, &txt->size, &flags,
+ &hor, &vert, &italic, &bold) != 9) {
+ free(s);
+ return 0;
+ }
+ txt->s = s;
+
+ if (flags) {
+/*
+ * @@@ visibility settings in component only seem to be used only as default
+ * for sheet and are ignored thereafter:
+ *
+ * || (comp->comp && !lib_field_visible(comp->comp, n))) {
+ */
+ free((char *) txt->s);
+ free(field);
+ return 1;
+ }
+
+ if (n == 0 && comp->comp && comp->comp->units > 1) {
+ len = strlen(txt->s);
+ s = realloc((void *) txt->s, len + 3);
+ if (!s)
+ diag_pfatal("realloc");
+ if (comp->unit <= 26)
+ sprintf(s + len, "%c", 'A' + comp->unit - 1);
+ else
+ sprintf(s + len, "%c%c",
+ 'A' + (comp->unit - 1) / 26 - 1,
+ 'A' + (comp->unit - 1) % 26);
+ txt->s = s;
+ }
+
+ field->next = comp->fields;
+ comp->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;
+}
+
+
+/* ----- Sheet field ------------------------------------------------------- */
+
+
+static enum dwg_shape decode_form(char form)
+{
+ switch (form) {
+ case 'O':
+ return dwg_in;
+ case 'I':
+ return dwg_out;
+ case 'B':
+ /* fall through */
+ case 'T':
+ return dwg_bidir;
+ case 'U':
+ return dwg_unspec;
+ default:
+ fatal("unknown form: \"%c\"\n", form);
+ }
+}
+
+
+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:
+ fatal("unknown side: \"%c\"\n", side);
+ }
+}
+
+
+static bool parse_hsheet_field(struct sch_ctx *ctx, const char *line)
+{
+ struct sch_sheet *sheet = &ctx->obj.u.sheet;
+ char *s;
+ char form, side;
+ unsigned n, dim;
+ struct sheet_field *field;
+
+ if (sscanf(line, "F%d \"%m[^\"]\" %u", &n, &s, &dim) == 3) {
+ switch (n) {
+ case 0:
+ sheet->name = s;
+ sheet->name_dim = dim;
+ return 1;
+ case 1:
+ sheet->file = s;
+ sheet->file_dim = dim;
+ return 1;
+ default:
+ assert(0);
+ }
+ }
+
+ field = alloc_type(struct sheet_field);
+ if (sscanf(line, "F%d \"%m[^\"]\" %c %c %d %d %u",
+ &n, &field->s, &form, &side, &field->x, &field->y, &field->dim)
+ != 7) {
+ free(field);
+ 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.
+ */
+ sheet->rotated = 1;
+ }
+ field->shape = decode_form(form);
+ field->side = decode_side(side);
+
+ field->next = sheet->fields;
+ sheet->fields = field;
+
+ return 1;
+}
+
+
+/* ----- Schematics parser ------------------------------------------------- */
+
+
+static struct sch_obj *submit_obj(struct sch_ctx *ctx, enum sch_obj_type type)
+{
+ struct sch_obj *obj;
+
+ obj = alloc_type(struct sch_obj);
+ *obj = ctx->obj;
+ obj->type = type;
+ obj->next = NULL;
+
+ *ctx->curr_sheet->next_obj = obj;
+ ctx->curr_sheet->next_obj = &obj->next;
+
+ return obj;
+}
+
+
+static struct sheet *new_sheet(struct sch_ctx *ctx)
+{
+ struct sheet *sheet;
+
+ sheet = alloc_type(struct sheet);
+ sheet->title = NULL;
+ sheet->objs = NULL;
+ sheet->next_obj = &sheet->objs;
+ sheet->next = NULL;
+
+ sheet->has_children = 0;
+
+ sheet->oid = NULL;
+
+ ctx->curr_sheet = sheet;
+
+ *ctx->next_sheet = sheet;
+ ctx->next_sheet = &sheet->next;
+
+ return sheet;
+}
+
+
+static bool parse_line(const struct file *file, void *user, const char *line);
+
+
+static struct sheet *recurse_sheet(struct sch_ctx *ctx,
+ const struct file *related)
+{
+ const char *name = ctx->obj.u.sheet.file;
+ struct sheet *parent, *sheet;
+ struct file file;
+ void *oid;
+ bool res;
+
+ if (!file_open(&file, name, related))
+ return NULL;
+
+ parent = ctx->curr_sheet;
+ sheet = new_sheet(ctx);
+ oid = file_oid(&file);
+ sheet->oid = oid;
+
+ if (ctx->prev && oid) {
+ const struct sheet *other;
+
+ for (other = ctx->prev->sheets; other; other = other->next)
+ if (!other->has_children &&
+ file_oid_eq(other->oid, oid)) {
+ ctx->curr_sheet = parent;
+ sheet->title = stralloc(other->title);
+ sheet->objs = other->objs;
+ return sheet;
+ }
+ }
+
+ ctx->state = sch_descr;
+ res = file_read(&file, parse_line, ctx);
+ file_close(&file);
+ if (!res)
+ return NULL; /* leave it to caller to clean up */
+
+ ctx->curr_sheet = parent;
+ parent->has_children = 1;
+
+ return sheet;
+}
+
+
+static bool parse_line(const struct file *file, void *user, const char *line)
+{
+ struct sch_ctx *ctx = user;
+ struct sch_obj *obj = &ctx->obj;
+ int n = 0;
+ char *s;
+
+ switch (ctx->state) {
+ case sch_basic:
+ if (sscanf(line, "$Comp%n", &n) == 0 && n) {
+ ctx->state = sch_comp;
+ obj->u.comp.fields = NULL;
+ return 1;
+ }
+ if (sscanf(line, "$Sheet%n", &n) == 0 && n) {
+ ctx->state = sch_sheet;
+ obj->u.sheet.name = NULL;
+ obj->u.sheet.file = NULL;
+ obj->u.sheet.rotated = 0;
+ obj->u.sheet.fields = NULL;
+ obj->u.sheet.sheet = NULL;
+ return 1;
+ }
+
+ /* Text */
+
+ struct sch_text *text = &obj->u.text;
+
+ if (sscanf(line, "Text Notes %d %d %d %d",
+ &obj->x, &obj->y, &text->dir, &text->dim) == 4) {
+ ctx->state = sch_text;
+ obj->u.text.fn = dwg_text;
+ obj->u.text.shape = dwg_unspec; /* not used for text */
+ return 1;
+ }
+ if (sscanf(line, "Text GLabel %d %d %d %d %ms",
+ &obj->x, &obj->y, &text->dir, &text->dim, &s) == 5) {
+ ctx->state = sch_text;
+ obj->u.text.fn = dwg_glabel;
+ obj->u.text.shape = decode_shape(s);
+ return 1;
+ }
+ if (sscanf(line, "Text HLabel %d %d %d %d %ms",
+ &obj->x, &obj->y, &text->dir, &text->dim, &s) == 5) {
+ ctx->state = sch_text;
+ obj->u.text.fn = dwg_hlabel;
+ obj->u.text.shape = decode_shape(s);
+ return 1;
+ }
+ if (sscanf(line, "Text Label %d %d %d %d",
+ &obj->x, &obj->y, &text->dir, &text->dim) == 4) {
+ ctx->state = sch_text;
+ obj->u.text.fn = dwg_label;
+ obj->u.text.shape = dwg_unspec;
+ /* not used for (local) labels */
+ return 1;
+ }
+
+ /* Connection */
+
+ if (sscanf(line, "Connection ~ %d %d", &obj->x, &obj->y) == 2) {
+ submit_obj(ctx, sch_obj_junction);
+ return 1;
+ }
+
+ /* NoConn */
+
+ if (sscanf(line, "NoConn ~ %d %d", &obj->x, &obj->y) == 2) {
+ submit_obj(ctx, sch_obj_noconn);
+ return 1;
+ }
+
+ /* Wire */
+
+ if (sscanf(line, "Wire Wire Line%n", &n) == 0 && n) {
+ ctx->state = sch_wire;
+ obj->u.wire.fn = dwg_wire;
+ return 1;
+ }
+ if (sscanf(line, "Wire Bus Line%n", &n) == 0 && n) {
+ ctx->state = sch_wire;
+ obj->u.wire.fn = dwg_bus;
+ return 1;
+ }
+ if (sscanf(line, "Wire Notes Line%n", &n) == 0 && n) {
+ ctx->state = sch_wire;
+ obj->u.wire.fn = dwg_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;
+ obj->u.wire.fn = dwg_wire;
+ return 1;
+ }
+ if (sscanf(line, "Entry Bus Bus%n", &n) == 0 && n) {
+ ctx->state = sch_wire;
+ obj->u.wire.fn = dwg_bus;
+ return 1;
+ }
+
+ /* EndSCHEMATC */
+
+ if (sscanf(line, "$EndSCHEMATC%n", &n) == 0 && n) {
+ ctx->state = sch_eof;
+ return 1;
+ }
+ break;
+ case sch_descr:
+ if (sscanf(line, "Title \"%m[^\"]\"", &s) == 1) {
+ ctx->curr_sheet->title = s;
+ return 1;
+ }
+ 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;
+ submit_obj(ctx, sch_obj_comp);
+ return 1;
+ }
+ if (sscanf(line, "L %ms", &s) == 1) {
+ obj->u.comp.comp = lib_find(ctx->lib, s);
+ free(s);
+ return 1;
+ }
+ if (sscanf(line, "U %u %u",
+ &obj->u.comp.unit, &obj->u.comp.convert) == 2)
+ return 1;
+ if (sscanf(line, "P %d %d", &obj->x, &obj->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",
+ obj->u.comp.m + 1, obj->u.comp.m + 2,
+ obj->u.comp.m + 4, obj->u.comp.m + 5);
+ if (n == 3)
+ return 1;
+ if (n == 4) {
+ obj->u.comp.m[0] = obj->x;
+ obj->u.comp.m[3] = obj->y;
+ return 1;
+ }
+ break;
+ case sch_sheet:
+ if (sscanf(line, "$EndSheet%n", &n) == 0 && n) {
+ struct sch_obj *sheet_obj;
+
+ sheet_obj = submit_obj(ctx, sch_obj_sheet);
+ sheet_obj->u.sheet.error = 0;
+ if (ctx->recurse) {
+ struct sheet *sheet;
+
+ sheet = recurse_sheet(ctx, file);
+ if (!sheet)
+ sheet_obj->u.sheet.error = 1;
+ if (sheet && sheet_obj->u.sheet.name) {
+ free((char *) sheet->title);
+ sheet->title =
+ stralloc(sheet_obj->u.sheet.name);
+ }
+ sheet_obj->u.sheet.sheet = sheet;
+ } else {
+ sheet_obj->u.sheet.sheet = NULL;
+ }
+ ctx->state = sch_basic;
+ return 1;
+ }
+ if (sscanf(line, "S %d %d %u %u",
+ &obj->x, &obj->y, &obj->u.sheet.w, &obj->u.sheet.h) == 4)
+ 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;
+ {
+ 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;
+ obj->u.text.s = s;
+ submit_obj(ctx, obj->u.text.fn == dwg_glabel ?
+ sch_obj_glabel : sch_obj_text);
+ }
+ return 1;
+ case sch_wire:
+ if (sscanf(line, "%d %d %d %d", &obj->x, &obj->y,
+ &obj->u.wire.ex, &obj->u.wire.ey) != 4)
+ break;
+ submit_obj(ctx, sch_obj_wire);
+ ctx->state = sch_basic;
+ return 1;
+ case sch_eof:
+ return 1;
+ default:
+ abort();
+ }
+ fatal("%s:%u: cannot parse\n\"%s\"\n", file->name, file->lineno, line);
+}
+
+
+bool sch_parse(struct sch_ctx *ctx, struct file *file, const struct lib *lib,
+ const struct sch_ctx *prev)
+{
+ ctx->lib = lib;
+ ctx->prev = prev;
+ return file_read(file, parse_line, ctx);
+}
+
+
+void sch_init(struct sch_ctx *ctx, bool recurse)
+{
+ ctx->state = sch_descr;
+ ctx->recurse = recurse;
+ ctx->curr_sheet = NULL;
+ ctx->sheets = NULL;
+ ctx->next_sheet = &ctx->sheets;
+ new_sheet(ctx);
+}
+
+
+static void free_comp_fields(struct comp_field *fields)
+{
+ struct comp_field *next;
+
+ while (fields) {
+ next = fields->next;
+ free((char *) fields->txt.s);
+ free(fields);
+ fields = next;
+ }
+}
+
+
+static void free_sheet_fields(struct sheet_field *fields)
+{
+ struct sheet_field *next;
+
+ while (fields) {
+ next = fields->next;
+ free((char *) fields->s);
+ free(fields);
+ fields = next;
+ }
+}
+
+
+static void free_sheet(struct sheet *sch)
+{
+ struct sch_obj *obj, *next;
+
+ if (!sch)
+ return;
+ free((char *) sch->title);
+ free(sch->oid);
+ for (obj = sch->objs; obj; obj = next) {
+ next = obj->next;
+ switch (obj->type) {
+ case sch_obj_glabel:
+ case sch_obj_text:
+ free((char *) obj->u.text.s);
+ break;
+ case sch_obj_comp:
+ free_comp_fields(obj->u.comp.fields);
+ break;
+ case sch_obj_sheet:
+ free((char *) obj->u.sheet.name);
+ free((char *) obj->u.sheet.file);
+ /*
+ * Caller frees all sheets, including this sub-sheet
+ * (obj->u.sheet.sheet), so we don't do this here.
+ */
+ free_sheet_fields(obj->u.sheet.fields);
+ break;
+ default:
+ break;
+ }
+ free(obj);
+ }
+ free(sch);
+}
+
+
+void sch_free(struct sch_ctx *ctx)
+{
+ struct sheet *next;
+
+ while (ctx->sheets) {
+ next = ctx->sheets->next;
+ free_sheet(ctx->sheets);
+ ctx->sheets = next;
+ }
+}
diff --git a/kicad/sch-render.c b/kicad/sch-render.c
new file mode 100644
index 0000000..34effb1
--- /dev/null
+++ b/kicad/sch-render.c
@@ -0,0 +1,177 @@
+/*
+ * kicad/sch-render.c - Render schematics
+ *
+ * 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 <stdio.h>
+#include <assert.h>
+
+#include "misc.h"
+#include "style.h"
+#include "gfx.h"
+#include "kicad/dwg.h"
+#include "kicad/lib.h"
+#include "kicad/sch.h"
+
+
+/* ----- Rendering --------------------------------------------------------- */
+
+
+static void dump_field(const struct comp_field *field, const 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 do_hsheet_text(const struct sch_obj *obj,
+ const struct sch_sheet *sheet)
+{
+ char *s;
+
+ assert(sheet->name && sheet->file);
+
+ struct text sheet_txt = {
+ .size = sheet->name_dim,
+ .x = obj->x,
+ .y = obj->y,
+ .rot = 0,
+ .hor = text_min,
+ .vert = text_min,
+ };
+ if (asprintf(&s, "Sheet: %s", sheet->name)) {}
+ sheet_txt.s = s; /* work around "const" mismatch */
+
+ struct text file_txt = {
+ .size = sheet->file_dim,
+ .x = obj->x,
+ .y = obj->y,
+ .rot = 0,
+ .hor = text_min,
+ .vert = text_max,
+ };
+ if (asprintf(&s, "File: %s", sheet->file)) {}
+ file_txt.s = s; /* work around "const" mismatch */
+
+ if (sheet->rotated) {
+ sheet_txt.rot = file_txt.rot = 90;
+ sheet_txt.x -= HSHEET_FIELD_OFFSET;
+ sheet_txt.y += sheet->h;
+ file_txt.x += sheet->w + HSHEET_FIELD_OFFSET;
+ file_txt.y += sheet->h;
+ } else {
+ sheet_txt.y -= HSHEET_FIELD_OFFSET;
+ file_txt.y += sheet->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);
+}
+
+
+static void render_sheet(const struct sch_obj *obj,
+ const struct sch_sheet *sheet)
+{
+ const struct sheet_field *field;
+
+ gfx_rect(obj->x, obj->y, obj->x + sheet->w, obj->y + sheet->h,
+ COLOR_HSHEET_BOX, sheet->error ? COLOR_MISSING_BG : COLOR_NONE,
+ LAYER_HSHEET_BOX);
+ do_hsheet_text(obj, sheet);
+
+ for (field = sheet->fields; field; field = field->next)
+ dwg_hlabel(field->x, field->y, field->s,
+ field->side, field->dim,
+ field->shape, NULL);
+ // free(field->s)
+}
+
+
+void sch_render(const struct sheet *sheet)
+{
+ struct sch_obj *obj;
+
+ for (obj = sheet->objs; obj; obj = obj->next)
+ switch (obj->type) {
+ case sch_obj_wire:
+ {
+ const struct sch_wire *wire = &obj->u.wire;
+
+ wire->fn(obj->x, obj->y, wire->ex, wire->ey);
+ }
+ break;
+ case sch_obj_junction:
+ dwg_junction(obj->x, obj->y);
+ break;
+ case sch_obj_noconn:
+ dwg_noconn(obj->x, obj->y);
+ break;
+ case sch_obj_glabel:
+ case sch_obj_text:
+ {
+ struct sch_text *text = &obj->u.text;
+
+ text->fn(obj->x, obj->y, text->s, text->dir,
+ text->dim, text->shape, &text->bbox);
+ }
+ break;
+ case sch_obj_comp:
+ {
+ const struct sch_comp *comp = &obj->u.comp;
+ const struct comp_field *field;
+
+ lib_render(comp->comp, comp->unit,
+ comp->convert, comp->m);
+ for (field = comp->fields; field;
+ field = field->next)
+ dump_field(field, comp->m);
+ }
+ break;
+ case sch_obj_sheet:
+ render_sheet(obj, &obj->u.sheet);
+ break;
+ }
+}
diff --git a/kicad/sch.h b/kicad/sch.h
new file mode 100644
index 0000000..d7e82f1
--- /dev/null
+++ b/kicad/sch.h
@@ -0,0 +1,138 @@
+/*
+ * kicad/sch.h - 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.
+ */
+
+
+#ifndef KICAD_SCH_H
+#define KICAD_SCH_H
+
+#include <stdbool.h>
+
+#include "dwg.h"
+#include "text.h"
+#include "file/file.h"
+#include "kicad/lib.h"
+
+
+enum sch_state {
+ sch_basic, /* basic state */
+ sch_descr, /* prelude and description */
+ sch_comp, /* component */
+ sch_sheet, /* sub-sheet */
+ sch_text, /* text or label */
+ sch_wire, /* wire */
+ sch_eof, /* skipping anything after $EndSCHEMATC */
+};
+
+struct sheet;
+
+struct sch_obj {
+ enum sch_obj_type {
+ sch_obj_wire,
+ sch_obj_junction,
+ sch_obj_noconn,
+ sch_obj_text,
+ sch_obj_glabel,
+ sch_obj_comp,
+ sch_obj_sheet,
+ } type;
+
+ int x, y;
+
+ union {
+ struct sch_wire {
+ void (*fn)(int sx, int sy, int ex, int ey);
+ int ex, ey;
+ } wire;
+ struct sch_text {
+ void (*fn)(int x, int y, const char *s,
+ int dir, int dim, enum dwg_shape shape,
+ struct dwg_bbox *bbox);
+ const char *s;
+ int dir; /* orientation */
+ int dim; /* dimension */
+ enum dwg_shape shape;
+ struct dwg_bbox bbox; /* set when rendering; glabel */
+ } text;
+ struct sch_comp {
+ const struct comp *comp; /* current component */
+ unsigned unit; /* unit of current component */
+ unsigned convert;/* "De Morgan" selection */
+ struct comp_field {
+ struct text txt;
+ struct comp_field *next;
+ } *fields;
+ int m[6];
+ } comp;
+ struct sch_sheet {
+ unsigned h, w;
+ const char *name;
+ unsigned name_dim;
+ const char *file;
+ unsigned file_dim;
+ bool rotated;
+ bool error; /* if set, sheet == NULL */
+ const struct sheet *sheet;
+ /* pointer to sub-sheet; NULL if absent */
+
+ struct sheet_field {
+ char *s;
+ int x, y;
+ unsigned dim;
+ enum dwg_shape shape;
+ unsigned side;
+ struct sheet_field *next;
+ } *fields;
+ } sheet;
+ } u;
+
+ struct sch_obj *next;
+};
+
+struct sheet {
+ const char *title; /* malloced, unless delta */
+ struct sch_obj *objs;
+ struct sch_obj **next_obj;
+ struct sheet *next;
+
+ bool has_children; /* aka sub-sheets */
+
+ /* caching */
+ void *oid;
+};
+
+struct sch_ctx {
+ enum sch_state state;
+
+ bool recurse;
+
+ struct sch_obj obj;
+
+ struct sheet *curr_sheet;
+ struct sheet *sheets;
+ struct sheet **next_sheet;
+
+ /* for caching */
+ const struct sch_ctx *prev;
+
+ const struct lib *lib;
+};
+
+
+void decode_alignment(struct text *txt, char hor, char vert);
+
+void sch_render(const struct sheet *sheet);
+bool sch_parse(struct sch_ctx *ctx, struct file *file, const struct lib *lib,
+ const struct sch_ctx *prev);
+void sch_init(struct sch_ctx *ctx, bool recurse);
+void sch_free(struct sch_ctx *ctx);
+
+#endif /* !KICAD_SCH_H */