Root/eeshow/kicad/sch-parse.c

1/*
2 * kicad/sch-parse.c - Parse Eeschema .sch file
3 *
4 * Written 2016 by Werner Almesberger
5 * Copyright 2016 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stddef.h>
15#include <stdbool.h>
16#include <stdlib.h>
17#include <stdio.h>
18#include <unistd.h>
19#include <string.h>
20#include <assert.h>
21
22#include "misc/util.h"
23#include "misc/diag.h"
24#include "kicad/dwg.h"
25#include "file/file.h"
26#include "kicad/lib.h"
27#include "kicad/sch.h"
28
29
30/* ----- (Global) Labels --------------------------------------------------- */
31
32
33static enum dwg_shape do_decode_shape(const char *s)
34{
35    if (!strcmp(s, "UnSpc"))
36        return dwg_unspec;
37    if (!strcmp(s, "Input"))
38        return dwg_in;
39    if (!strcmp(s, "Output"))
40        return dwg_out;
41    if (!strcmp(s, "3State"))
42        return dwg_tri;
43    if (!strcmp(s, "BiDi"))
44        return dwg_bidir;
45    fatal("unknown shape: \"%s\"\n", s);
46}
47
48
49static enum dwg_shape decode_shape(const char *s)
50{
51    enum dwg_shape res;
52
53    res = do_decode_shape(s);
54    free((void *) s);
55    return res;
56}
57
58
59/* ----- Component fields -------------------------------------------------- */
60
61
62void decode_alignment(struct text *txt, char hor, char vert)
63{
64    switch (hor) {
65    case 'L':
66        txt->hor = text_min;
67        break;
68    case 'C':
69        txt->hor = text_mid;
70        break;
71    case 'R':
72        txt->hor = text_max;
73        break;
74    default:
75        assert(0);
76    }
77
78    switch (vert) {
79    case 'B':
80        txt->vert = text_min;
81        break;
82    case 'C':
83        txt->vert = text_mid;
84        break;
85    case 'T':
86        txt->vert = text_max;
87        break;
88    default:
89        assert(0);
90    }
91}
92
93
94static bool parse_field(struct sch_ctx *ctx, const char *line)
95{
96    struct sch_comp *comp = &ctx->obj.u.comp;
97    int n;
98    unsigned flags;
99    char hv, hor, vert, italic, bold;
100    struct comp_field *field;
101    struct text *txt;
102    char *s;
103    const char *p;
104    int pos = 0, len;
105
106    field = alloc_type(struct comp_field);
107    txt = &field->txt;
108    txt->s = NULL;
109
110    if (sscanf(line, "F %d \"\" %c %d %d %u %u %c %c%c%c",
111        &n, &hv, &txt->x, &txt->y, &txt->size, &flags, &hor, &vert,
112        &italic, &bold) == 10) {
113        free(field);
114        return 1;
115    }
116
117    if (sscanf(line, "F %d \"%n", &n, &pos) != 1 || !pos)
118        return 0;
119    for (p = line + pos; *p && *p != '"'; p++)
120        if (*p == '\\' && p[1])
121            p++;
122    if (*p != '"')
123        return 0;
124
125    len = p - (line + pos);
126    s = alloc_size(len + 1);
127    memcpy(s, line + pos, len);
128    s[len] = 0;
129
130    if (sscanf(p + 1, " %c %d %d %u %u %c %c%c%c",
131        &hv, &txt->x, &txt->y, &txt->size, &flags,
132        &hor, &vert, &italic, &bold) != 9) {
133        free(s);
134        return 0;
135    }
136    txt->s = s;
137
138    if (flags) {
139/*
140 * @@@ visibility settings in component only seem to be used only as default
141 * for sheet and are ignored thereafter:
142 *
143 * || (comp->comp && !lib_field_visible(comp->comp, n))) {
144 */
145        free((char *) txt->s);
146        free(field);
147        return 1;
148    }
149
150    if (n == 0 && comp->comp && comp->comp->units > 1) {
151        len = strlen(txt->s);
152        s = realloc_size((void *) txt->s, len + 3);
153        if (comp->unit <= 26)
154            sprintf(s + len, "%c", 'A' + comp->unit - 1);
155        else
156            sprintf(s + len, "%c%c",
157                'A' + (comp->unit - 1) / 26 - 1,
158                'A' + (comp->unit - 1) % 26);
159        txt->s = s;
160    }
161
162    field->next = comp->fields;
163    comp->fields = field;
164
165    switch (hv) {
166    case 'H':
167        txt->rot = 0;
168        break;
169    case 'V':
170        txt->rot = 90;
171        break;
172    default:
173        assert(0);
174    }
175
176    decode_alignment(txt, hor, vert);
177
178    // @@@ decode font
179
180    return 1;
181}
182
183
184/* ----- Sheet field ------------------------------------------------------- */
185
186
187static enum dwg_shape decode_form(char form)
188{
189    switch (form) {
190    case 'O':
191        return dwg_in;
192    case 'I':
193        return dwg_out;
194    case 'B':
195        /* fall through */
196    case 'T':
197        return dwg_bidir;
198    case 'U':
199        return dwg_unspec;
200    default:
201        fatal("unknown form: \"%c\"\n", form);
202    }
203}
204
205
206static int decode_side(char side)
207{
208    switch (side) {
209    case 'L':
210        return 2; /* left */
211    case 'B':
212        return 1; /* up */
213    case 'R':
214        return 0; /* right */
215    case 'T':
216        return 3; /* down */
217    default:
218        fatal("unknown side: \"%c\"\n", side);
219    }
220}
221
222
223static bool parse_hsheet_field(struct sch_ctx *ctx, const char *line)
224{
225    struct sch_sheet *sheet = &ctx->obj.u.sheet;
226    char *s;
227    char form, side;
228    unsigned n, dim;
229    struct sheet_field *field;
230
231    if (sscanf(line, "F%d \"%m[^\"]\" %u", &n, &s, &dim) == 3) {
232        switch (n) {
233        case 0:
234            sheet->name = s;
235            sheet->name_dim = dim;
236            return 1;
237        case 1:
238            sheet->file = s;
239            sheet->file_dim = dim;
240            return 1;
241        default:
242            assert(0);
243        }
244    }
245
246    field = alloc_type(struct sheet_field);
247    if (sscanf(line, "F%d \"%m[^\"]\" %c %c %d %d %u",
248        &n, &field->s, &form, &side, &field->x, &field->y, &field->dim)
249        != 7) {
250        free(field);
251        return 0;
252    }
253    assert(n >= 2);
254
255    if (side == 'B' || side == 'T') {
256        /*
257         * This is beautiful: since there is no indication for rotation
258         * on the hsheet, or the sheet or file fields, we need to look
259         * at whether the imported sheet pins go left or right (no
260         * rotation) or whether they go top or bottom (rotation).
261         *
262         * A sheet with no imported pins lacks these hints, and is
263         * therefore always assumed to be without rotation.
264         *
265         * Eeschema is careful to be consistent, and does not allow
266         * sheets with no imported pins to be rotated. Even better, it
267         * flips rotated sheets where the last imported pin is deleted
268         * back.
269         */
270        sheet->rotated = 1;
271    }
272    field->shape = decode_form(form);
273    field->side = decode_side(side);
274
275    field->next = sheet->fields;
276    sheet->fields = field;
277
278    return 1;
279}
280
281
282/* ----- Schematics parser ------------------------------------------------- */
283
284
285static struct sch_obj *submit_obj(struct sch_ctx *ctx, enum sch_obj_type type)
286{
287    struct sch_obj *obj;
288
289    obj = alloc_type(struct sch_obj);
290    *obj = ctx->obj;
291    obj->type = type;
292    obj->next = NULL;
293
294    *ctx->curr_sheet->next_obj = obj;
295    ctx->curr_sheet->next_obj = &obj->next;
296
297    return obj;
298}
299
300
301static struct sheet *new_sheet(struct sch_ctx *ctx)
302{
303    struct sheet *sheet;
304
305    sheet = alloc_type(struct sheet);
306    sheet->title = NULL;
307    sheet->objs = NULL;
308    sheet->next_obj = &sheet->objs;
309    sheet->next = NULL;
310
311    sheet->w = sheet->h = 0;
312
313    sheet->has_children = 0;
314
315    sheet->oid = NULL;
316
317    ctx->curr_sheet = sheet;
318
319    *ctx->next_sheet = sheet;
320    ctx->next_sheet = &sheet->next;
321
322    return sheet;
323}
324
325
326static bool parse_line(const struct file *file, void *user, const char *line);
327
328
329static struct sheet *recurse_sheet(struct sch_ctx *ctx,
330    const struct file *related)
331{
332    const char *name = ctx->obj.u.sheet.file;
333    struct sheet *parent, *sheet;
334    struct file file;
335    void *oid;
336    bool res;
337
338    if (!file_open(&file, name, related))
339        return NULL;
340
341    parent = ctx->curr_sheet;
342    sheet = new_sheet(ctx);
343    oid = file_oid(&file);
344    sheet->oid = oid;
345
346    if (ctx->prev && oid) {
347        const struct sheet *other;
348
349        for (other = ctx->prev->sheets; other; other = other->next)
350            if (!other->has_children &&
351                file_oid_eq(other->oid, oid)) {
352                ctx->curr_sheet = parent;
353                sheet->title = stralloc(other->title);
354                sheet->objs = other->objs;
355                sheet->w = other->w;
356                sheet->h = other->h;
357                return sheet;
358            }
359    }
360
361    ctx->state = sch_descr;
362    res = file_read(&file, parse_line, ctx);
363    file_close(&file);
364    if (!res)
365        return NULL; /* leave it to caller to clean up */
366
367    ctx->curr_sheet = parent;
368    parent->has_children = 1;
369
370    return sheet;
371}
372
373
374static bool parse_line(const struct file *file, void *user, const char *line)
375{
376    struct sch_ctx *ctx = user;
377    struct sch_obj *obj = &ctx->obj;
378    int n = 0;
379    char *s;
380
381    switch (ctx->state) {
382    case sch_basic:
383        if (sscanf(line, "$Comp%n", &n) == 0 && n) {
384            ctx->state = sch_comp;
385            obj->u.comp.fields = NULL;
386            return 1;
387        }
388        if (sscanf(line, "$Sheet%n", &n) == 0 && n) {
389            ctx->state = sch_sheet;
390            obj->u.sheet.name = NULL;
391            obj->u.sheet.file = NULL;
392            obj->u.sheet.rotated = 0;
393            obj->u.sheet.fields = NULL;
394            obj->u.sheet.sheet = NULL;
395            return 1;
396        }
397
398        /* Text */
399
400        struct sch_text *text = &obj->u.text;
401
402        if (sscanf(line, "Text Notes %d %d %d %d",
403            &obj->x, &obj->y, &text->dir, &text->dim) == 4) {
404            ctx->state = sch_text;
405            obj->u.text.fn = dwg_text;
406            obj->u.text.shape = dwg_unspec; /* not used for text */
407            return 1;
408        }
409        if (sscanf(line, "Text GLabel %d %d %d %d %ms",
410            &obj->x, &obj->y, &text->dir, &text->dim, &s) == 5) {
411            ctx->state = sch_text;
412            obj->u.text.fn = dwg_glabel;
413            obj->u.text.shape = decode_shape(s);
414            return 1;
415        }
416        if (sscanf(line, "Text HLabel %d %d %d %d %ms",
417            &obj->x, &obj->y, &text->dir, &text->dim, &s) == 5) {
418            ctx->state = sch_text;
419            obj->u.text.fn = dwg_hlabel;
420            obj->u.text.shape = decode_shape(s);
421            return 1;
422        }
423        if (sscanf(line, "Text Label %d %d %d %d",
424            &obj->x, &obj->y, &text->dir, &text->dim) == 4) {
425            ctx->state = sch_text;
426            obj->u.text.fn = dwg_label;
427            obj->u.text.shape = dwg_unspec;
428                /* not used for (local) labels */
429            return 1;
430        }
431
432        /* Connection */
433
434        if (sscanf(line, "Connection ~ %d %d", &obj->x, &obj->y) == 2) {
435            submit_obj(ctx, sch_obj_junction);
436            return 1;
437        }
438
439        /* NoConn */
440
441        if (sscanf(line, "NoConn ~ %d %d", &obj->x, &obj->y) == 2) {
442            submit_obj(ctx, sch_obj_noconn);
443            return 1;
444        }
445
446        /* Wire */
447
448        if (sscanf(line, "Wire Wire Line%n", &n) == 0 && n) {
449            ctx->state = sch_wire;
450            obj->u.wire.fn = dwg_wire;
451            return 1;
452        }
453        if (sscanf(line, "Wire Bus Line%n", &n) == 0 && n) {
454            ctx->state = sch_wire;
455            obj->u.wire.fn = dwg_bus;
456            return 1;
457        }
458        if (sscanf(line, "Wire Notes Line%n", &n) == 0 && n) {
459            ctx->state = sch_wire;
460            obj->u.wire.fn = dwg_line;
461            return 1;
462        }
463
464        /* Entry */
465
466        /*
467         * Documentation mentions the following additional variants:
468         *
469         * - Entry Wire Line equivalent:
470         * Wire Wire Bus
471         * Entry Wire Bus
472         *
473         * - Entry Bus Bus equivalent:
474         * Wire Bus Bus
475         */
476        if (sscanf(line, "Entry Wire Line%n", &n) == 0 && n) {
477            ctx->state = sch_wire;
478            obj->u.wire.fn = dwg_wire;
479            return 1;
480        }
481        if (sscanf(line, "Entry Bus Bus%n", &n) == 0 && n) {
482            ctx->state = sch_wire;
483            obj->u.wire.fn = dwg_bus;
484            return 1;
485        }
486
487        /* EndSCHEMATC */
488
489        if (sscanf(line, "$EndSCHEMATC%n", &n) == 0 && n) {
490            ctx->state = sch_eof;
491            return 1;
492        }
493        break;
494    case sch_descr:
495        if (sscanf(line, "$Descr %*s %d %d",
496            &ctx->curr_sheet->w, &ctx->curr_sheet->h) == 2)
497            return 1;
498        if (sscanf(line, "Title \"%m[^\"]\"", &s) == 1) {
499            ctx->curr_sheet->title = s;
500            return 1;
501        }
502        if (sscanf(line, "$EndDescr%n", &n) || !n)
503            return 1;
504        ctx->state = sch_basic;
505        return 1;
506    case sch_comp:
507        if (sscanf(line, "$EndComp%n", &n) == 0 && n) {
508            ctx->state = sch_basic;
509            submit_obj(ctx, sch_obj_comp);
510            return 1;
511        }
512        if (sscanf(line, "L %ms", &s) == 1) {
513            obj->u.comp.comp = lib_find(ctx->lib, s);
514            free(s);
515            return 1;
516        }
517        if (sscanf(line, "U %u %u",
518            &obj->u.comp.unit, &obj->u.comp.convert) == 2)
519            return 1;
520        if (sscanf(line, "P %d %d", &obj->x, &obj->y) == 2)
521            return 1;
522        if (parse_field(ctx, line))
523            return 1;
524        if (sscanf(line, "AR %n", &n) == 0 && n)
525            return 1; /* @@@ what is "AR" ? */
526        n = sscanf(line, " %d %d %d %d",
527            obj->u.comp.m + 1, obj->u.comp.m + 2,
528            obj->u.comp.m + 4, obj->u.comp.m + 5);
529        if (n == 3)
530            return 1;
531        if (n == 4) {
532            obj->u.comp.m[0] = obj->x;
533            obj->u.comp.m[3] = obj->y;
534            return 1;
535        }
536        break;
537    case sch_sheet:
538        if (sscanf(line, "$EndSheet%n", &n) == 0 && n) {
539            struct sch_obj *sheet_obj;
540
541            sheet_obj = submit_obj(ctx, sch_obj_sheet);
542            sheet_obj->u.sheet.error = 0;
543            if (ctx->recurse) {
544                struct sheet *sheet;
545
546                sheet = recurse_sheet(ctx, file);
547                if (!sheet)
548                    sheet_obj->u.sheet.error = 1;
549                if (sheet && sheet_obj->u.sheet.name) {
550                    free((char *) sheet->title);
551                    sheet->title =
552                        stralloc(sheet_obj->u.sheet.name);
553                }
554                sheet_obj->u.sheet.sheet = sheet;
555            } else {
556                sheet_obj->u.sheet.sheet = NULL;
557            }
558            ctx->state = sch_basic;
559            return 1;
560        }
561        if (sscanf(line, "S %d %d %u %u",
562            &obj->x, &obj->y, &obj->u.sheet.w, &obj->u.sheet.h) == 4)
563            return 1;
564        if (sscanf(line, "U %*x%n", &n) == 0 && n)
565            return 1;
566        if (parse_hsheet_field(ctx, line))
567            return 1;
568        break;
569    case sch_text:
570        ctx->state = sch_basic;
571        {
572            const char *from;
573            char *to;
574
575            s = alloc_size(strlen(line) + 1);
576            from = line;
577            to = s;
578            while (*from) {
579                if (from[0] != '\\' || from[1] != 'n') {
580                    *to++ = *from++;
581                    continue;
582                }
583                *to++ = '\n';
584                from += 2;
585            }
586            *to = 0;
587            obj->u.text.s = s;
588            submit_obj(ctx, obj->u.text.fn == dwg_glabel ?
589                sch_obj_glabel : sch_obj_text);
590        }
591        return 1;
592    case sch_wire:
593        if (sscanf(line, "%d %d %d %d", &obj->x, &obj->y,
594            &obj->u.wire.ex, &obj->u.wire.ey) != 4)
595            break;
596        submit_obj(ctx, sch_obj_wire);
597        ctx->state = sch_basic;
598        return 1;
599    case sch_eof:
600        return 1;
601    default:
602        abort();
603    }
604    fatal("%s:%u: cannot parse\n\"%s\"\n", file->name, file->lineno, line);
605}
606
607
608bool sch_parse(struct sch_ctx *ctx, struct file *file, const struct lib *lib,
609    const struct sch_ctx *prev)
610{
611    ctx->lib = lib;
612    ctx->prev = prev;
613    return file_read(file, parse_line, ctx);
614}
615
616
617void sch_init(struct sch_ctx *ctx, bool recurse)
618{
619    ctx->state = sch_descr;
620    ctx->recurse = recurse;
621    ctx->curr_sheet = NULL;
622    ctx->sheets = NULL;
623    ctx->next_sheet = &ctx->sheets;
624    new_sheet(ctx);
625}
626
627
628static void free_comp_fields(struct comp_field *fields)
629{
630    struct comp_field *next;
631
632    while (fields) {
633        next = fields->next;
634        free((char *) fields->txt.s);
635        free(fields);
636        fields = next;
637    }
638}
639
640
641static void free_sheet_fields(struct sheet_field *fields)
642{
643    struct sheet_field *next;
644
645    while (fields) {
646        next = fields->next;
647        free((char *) fields->s);
648        free(fields);
649        fields = next;
650    }
651}
652
653
654static void free_sheet(struct sheet *sch)
655{
656    struct sch_obj *obj, *next;
657
658    if (!sch)
659        return;
660    free((char *) sch->title);
661    free(sch->oid);
662    for (obj = sch->objs; obj; obj = next) {
663        next = obj->next;
664        switch (obj->type) {
665        case sch_obj_glabel:
666        case sch_obj_text:
667            free((char *) obj->u.text.s);
668            break;
669        case sch_obj_comp:
670            free_comp_fields(obj->u.comp.fields);
671            break;
672        case sch_obj_sheet:
673            free((char *) obj->u.sheet.name);
674            free((char *) obj->u.sheet.file);
675            /*
676             * Caller frees all sheets, including this sub-sheet
677             * (obj->u.sheet.sheet), so we don't do this here.
678             */
679            free_sheet_fields(obj->u.sheet.fields);
680            break;
681        default:
682            break;
683        }
684        free(obj);
685    }
686    free(sch);
687}
688
689
690void sch_free(struct sch_ctx *ctx)
691{
692    struct sheet *next;
693
694    while (ctx->sheets) {
695        next = ctx->sheets->next;
696        free_sheet(ctx->sheets);
697        ctx->sheets = next;
698    }
699}
700

Archive Download this file

Branches:
master



interactive