1 /*
2 * load-schematic.c
3 *
4 *
5 * Authors:
6 * Richard Hult <rhult@hem.passagen.se>
7 * Ricardo Markiewicz <rmarkie@fi.uba.ar>
8 * Andres de Barbara <adebarbara@fi.uba.ar>
9 * Marc Lorber <lorber.marc@wanadoo.fr>
10 * Bernhard Schuster <bernhard@ahoi.io>
11 * Guido Trentalancia <guido@trentalancia.com>
12 *
13 * Web page: https://ahoi.io/project/oregano
14 *
15 * Copyright (C) 1999-2001 Richard Hult
16 * Copyright (C) 2003,2006 Ricardo Markiewicz
17 * Copyright (C) 2009-2012 Marc Lorber
18 * Copyright (C) 2013-2014 Bernhard Schuster
19 * Copyright (C) 2017 Guido Trentalancia
20 *
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License as
23 * published by the Free Software Foundation; either version 2 of the
24 * License, or (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 * General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public
32 * License along with this program; if not, write to the
33 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
34 * Boston, MA 02110-1301, USA.
35 */
36
37 #include <string.h>
38 #include <glib/gi18n.h>
39
40 #include "xml-compat.h"
41 #include "oregano.h"
42 #include "xml-helper.h"
43 #include "load-common.h"
44 #include "load-schematic.h"
45 #include "coords.h"
46 #include "part-label.h"
47 #include "schematic.h"
48 #include "sim-settings.h"
49 #include "textbox.h"
50 #include "errors.h"
51 #include "engines/netlist-helper.h"
52
53 #include "debug.h"
54
55 typedef enum {
56 PARSE_START,
57 PARSE_SCHEMATIC,
58 PARSE_TITLE,
59 PARSE_AUTHOR,
60 PARSE_VERSION,
61 PARSE_COMMENTS,
62
63 PARSE_SIMULATION_SETTINGS,
64 PARSE_TRANSIENT_SETTINGS,
65 PARSE_TRANSIENT_ENABLED,
66 PARSE_TRANSIENT_START,
67 PARSE_TRANSIENT_STOP,
68 PARSE_TRANSIENT_STEP,
69 PARSE_TRANSIENT_STEP_ENABLE,
70 PARSE_TRANSIENT_INIT_COND,
71 PARSE_TRANSIENT_ANALYZE_ALL,
72
73 PARSE_AC_SETTINGS,
74 PARSE_AC_ENABLED,
75 PARSE_AC_VOUT,
76 PARSE_AC_TYPE,
77 PARSE_AC_NPOINTS,
78 PARSE_AC_START,
79 PARSE_AC_STOP,
80
81 PARSE_DC_SETTINGS,
82 PARSE_DC_ENABLED,
83 PARSE_DC_VSRC,
84 PARSE_DC_VOUT,
85 PARSE_DC_START,
86 PARSE_DC_STOP,
87 PARSE_DC_STEP,
88
89 PARSE_FOURIER_SETTINGS,
90 PARSE_FOURIER_ENABLED,
91 PARSE_FOURIER_FREQ,
92 PARSE_FOURIER_VOUT,
93
94 PARSE_NOISE_SETTINGS,
95 PARSE_NOISE_ENABLED,
96 PARSE_NOISE_VSRC,
97 PARSE_NOISE_VOUT,
98 PARSE_NOISE_TYPE,
99 PARSE_NOISE_NPOINTS,
100 PARSE_NOISE_START,
101 PARSE_NOISE_STOP,
102
103 PARSE_OPTION_LIST,
104 PARSE_OPTION,
105 PARSE_OPTION_NAME,
106 PARSE_OPTION_VALUE,
107
108 PARSE_ZOOM,
109
110 PARSE_PARTS,
111 PARSE_PART,
112 PARSE_PART_NAME,
113 PARSE_PART_LIBNAME,
114 PARSE_PART_REFDES,
115 PARSE_PART_POSITION,
116 PARSE_PART_ROTATION,
117 PARSE_PART_FLIP,
118 PARSE_PART_SYMNAME,
119 PARSE_PART_TEMPLATE,
120 PARSE_PART_MODEL,
121 PARSE_PART_LABELS,
122 PARSE_PART_LABEL,
123 PARSE_PART_LABEL_NAME,
124 PARSE_PART_LABEL_TEXT,
125 PARSE_PART_LABEL_POS,
126 PARSE_PART_PROPERTIES,
127 PARSE_PART_PROPERTY,
128 PARSE_PART_PROPERTY_NAME,
129 PARSE_PART_PROPERTY_VALUE,
130 PARSE_WIRES,
131 PARSE_WIRE,
132 PARSE_WIRE_POINTS,
133 // PARSE_WIRE_LABEL,
134 PARSE_TEXTBOXES,
135 PARSE_TEXTBOX,
136 PARSE_TEXTBOX_TEXT,
137 PARSE_TEXTBOX_POSITION,
138 PARSE_FINISH,
139 PARSE_UNKNOWN,
140 PARSE_ERROR
141 } State;
142
143 typedef struct
144 {
145 State state;
146 State prev_state;
147 int unknown_depth;
148 GString *content;
149 Schematic *schematic;
150 SimSettings *sim_settings;
151
152 char *author;
153 char *title;
154 char *oregano_version;
155 char *comments;
156
157 char *textbox_text;
158
159 // Temporary place holder for a wire
160 Coords wire_start;
161 Coords wire_end;
162
163 // Temporary place holder for a part
164 LibraryPart *part;
165 PartLabel *label;
166 Property *property;
167 Coords pos;
168 int rotation;
169 IDFlip flip;
170
171 // Temporary place holder for an option
172 SimOption *option;
173 } ParseState;
174
175 static xmlEntityPtr get_entity (void *user_data, const xmlChar *name);
176 static void start_document (ParseState *state);
177 static void end_document (ParseState *state);
178 static void start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs);
179 static void end_element (ParseState *state, const xmlChar *name);
180
181 static void my_characters (ParseState *state, const xmlChar *chars, int len);
182 static void my_warning (void *user_data, const char *msg, ...);
183 static void my_error (void *user_data, const char *msg, ...);
184 static void my_fatal_error (void *user_data, const char *msg, ...);
185
186 static void create_wire (ParseState *state);
187 static void create_part (ParseState *state);
188
189 static xmlSAXHandler oreganoSAXParser = {
190 0, // internalSubset
191 0, // isStandalone
192 0, // hasInternalSubset
193 0, // hasExternalSubset
194 0, // resolveEntity
195 (getEntitySAXFunc)get_entity, // getEntity
196 0, // entityDecl
197 0, // notationDecl
198 0, // attributeDecl
199 0, // elementDecl
200 0, // unparsedEntityDecl
201 0, // setDocumentLocator
202 (startDocumentSAXFunc)start_document, // startDocument
203 (endDocumentSAXFunc)end_document, // endDocument
204 (startElementSAXFunc)start_element, // startElement
205 (endElementSAXFunc)end_element, // endElement
206 0, // reference
207 (charactersSAXFunc)my_characters, // characters
208 0, // ignorableWhitespace
209 0, // processingInstruction
210 0, //(commentSAXFunc)0, comment
211 (warningSAXFunc)my_warning, // warning
212 (errorSAXFunc)my_error, // error
213 (fatalErrorSAXFunc)my_fatal_error, // fatalError
214 };
215
create_textbox(ParseState * state)216 static void create_textbox (ParseState *state)
217 {
218 Textbox *textbox;
219
220 textbox = textbox_new (NULL);
221 textbox_set_text (textbox, state->textbox_text);
222 item_data_set_pos (ITEM_DATA (textbox), &state->pos);
223 schematic_add_item (state->schematic, ITEM_DATA (textbox));
224 }
225
create_wire(ParseState * state)226 static void create_wire (ParseState *state)
227 {
228 Coords start_pos, length;
229 Wire *wire;
230
231 start_pos.x = state->wire_start.x;
232 start_pos.y = state->wire_start.y;
233 length.x = state->wire_end.x - start_pos.x;
234 length.y = state->wire_end.y - start_pos.y;
235
236 wire = wire_new ();
237 wire_set_length (wire, &length);
238
239 item_data_set_pos (ITEM_DATA (wire), &state->wire_start);
240 schematic_add_item (state->schematic, ITEM_DATA (wire));
241 }
242
create_part(ParseState * state)243 static void create_part (ParseState *state)
244 {
245 Part *part;
246 LibraryPart *library_part = state->part;
247
248 part = part_new_from_library_part (library_part);
249 if (!part) {
250 g_warning ("Failed to create Part from LibraryPart");
251 return;
252 }
253
254 item_data_set_pos (ITEM_DATA (part), &state->pos);
255 item_data_rotate (ITEM_DATA (part), state->rotation, NULL);
256 if (state->flip & ID_FLIP_HORIZ)
257 item_data_flip (ITEM_DATA (part), ID_FLIP_HORIZ, NULL);
258 if (state->flip & ID_FLIP_VERT)
259 item_data_flip (ITEM_DATA (part), ID_FLIP_VERT, NULL);
260
261 schematic_add_item (state->schematic, ITEM_DATA (part));
262 }
263
schematic_parse_xml_file(Schematic * sm,const char * filename,GError ** error)264 int schematic_parse_xml_file (Schematic *sm, const char *filename, GError **error)
265 {
266 ParseState state;
267 int retval = 0;
268
269 state.schematic = sm;
270 state.sim_settings = schematic_get_sim_settings (sm);
271 state.author = NULL;
272 state.title = NULL;
273 state.oregano_version = NULL;
274 state.comments = NULL;
275
276 if (!oreganoXmlSAXParseFile (&oreganoSAXParser, &state, filename)) {
277 g_warning ("Document not well formed!");
278 if (error != NULL) {
279 g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_BAD_FILE_FORMAT,
280 _ ("Bad file format."));
281 }
282 retval = -1;
283 }
284
285 if (state.state == PARSE_ERROR) {
286 if (error != NULL) {
287 g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_BAD_FILE_FORMAT,
288 _ ("Unknown parser error."));
289 }
290 retval = -2;
291 }
292
293 schematic_set_filename(sm, filename);
294 schematic_set_author (sm, state.author);
295 schematic_set_title (sm, state.title);
296 schematic_set_version(sm, state.oregano_version);
297 schematic_set_comments (sm, state.comments);
298
299 g_free(state.author);
300 g_free(state.title);
301 g_free(state.oregano_version);
302 g_free(state.comments);
303
304 update_schematic(sm);
305
306 return retval;
307 }
308
start_document(ParseState * state)309 static void start_document (ParseState *state)
310 {
311 state->state = PARSE_START;
312 state->unknown_depth = 0;
313 state->prev_state = PARSE_UNKNOWN;
314
315 state->content = g_string_sized_new (128);
316 state->part = NULL;
317 }
318
end_document(ParseState * state)319 static void end_document (ParseState *state)
320 {
321
322 if (state->unknown_depth != 0)
323 g_warning ("unknown_depth != 0 (%d)", state->unknown_depth);
324 }
325
start_element(ParseState * state,const xmlChar * name,const xmlChar ** attrs)326 static void start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs)
327 {
328 switch (state->state) {
329 case PARSE_START:
330 if (xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:schematic")) {
331 g_warning ("Expecting 'ogo:schematic'. Got '%s'", name);
332 state->state = PARSE_ERROR;
333 } else
334 state->state = PARSE_SCHEMATIC;
335 break;
336
337 case PARSE_SCHEMATIC:
338 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:author")) {
339 state->state = PARSE_AUTHOR;
340 g_string_truncate (state->content, 0);
341 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:title")) {
342 state->state = PARSE_TITLE;
343 g_string_truncate (state->content, 0);
344 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:version")) {
345 state->state = PARSE_VERSION;
346 g_string_truncate (state->content, 0);
347 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:comments")) {
348 state->state = PARSE_COMMENTS;
349 g_string_truncate (state->content, 0);
350 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:zoom")) {
351 state->state = PARSE_ZOOM;
352 g_string_truncate (state->content, 0);
353 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:simulation-settings")) {
354 state->state = PARSE_SIMULATION_SETTINGS;
355 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:parts")) {
356 state->state = PARSE_PARTS;
357 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:wires")) {
358 state->state = PARSE_WIRES;
359 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:textboxes")) {
360 state->state = PARSE_TEXTBOXES;
361 } else {
362 state->prev_state = state->state;
363 state->state = PARSE_UNKNOWN;
364 state->unknown_depth++;
365 }
366 break;
367
368 case PARSE_SIMULATION_SETTINGS:
369 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:transient")) {
370 state->state = PARSE_TRANSIENT_SETTINGS;
371 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:ac")) {
372 state->state = PARSE_AC_SETTINGS;
373 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:dc-sweep")) {
374 state->state = PARSE_DC_SETTINGS;
375 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:fourier")) {
376 state->state = PARSE_FOURIER_SETTINGS;
377 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:noise")) {
378 state->state = PARSE_NOISE_SETTINGS;
379 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:options")) {
380 state->state = PARSE_OPTION_LIST;
381 } else {
382 state->prev_state = state->state;
383 state->state = PARSE_UNKNOWN;
384 state->unknown_depth++;
385 }
386 break;
387
388 case PARSE_TRANSIENT_SETTINGS:
389 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:enabled")) {
390 state->state = PARSE_TRANSIENT_ENABLED;
391 g_string_truncate (state->content, 0);
392 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:start")) {
393 state->state = PARSE_TRANSIENT_START;
394 g_string_truncate (state->content, 0);
395 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:stop")) {
396 state->state = PARSE_TRANSIENT_STOP;
397 g_string_truncate (state->content, 0);
398 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:step")) {
399 state->state = PARSE_TRANSIENT_STEP;
400 g_string_truncate (state->content, 0);
401 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:step-enabled")) {
402 state->state = PARSE_TRANSIENT_STEP_ENABLE;
403 g_string_truncate (state->content, 0);
404 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:init-conditions")) {
405 state->state = PARSE_TRANSIENT_INIT_COND;
406 g_string_truncate (state->content, 0);
407 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:analyze-all")) {
408 state->state = PARSE_TRANSIENT_ANALYZE_ALL;
409 g_string_truncate (state->content, 0);
410 } else {
411 state->prev_state = state->state;
412 state->state = PARSE_UNKNOWN;
413 state->unknown_depth++;
414 }
415 break;
416
417 case PARSE_AC_SETTINGS:
418 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:enabled")) {
419 state->state = PARSE_AC_ENABLED;
420 g_string_truncate (state->content, 0);
421 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout1")) {
422 state->state = PARSE_AC_VOUT;
423 g_string_truncate (state->content, 0);
424 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:type")) {
425 state->state = PARSE_AC_TYPE;
426 g_string_truncate (state->content, 0);
427 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:npoints")) {
428 state->state = PARSE_AC_NPOINTS;
429 g_string_truncate (state->content, 0);
430 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:start")) {
431 state->state = PARSE_AC_START;
432 g_string_truncate (state->content, 0);
433 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:stop")) {
434 state->state = PARSE_AC_STOP;
435 g_string_truncate (state->content, 0);
436 } else {
437 state->prev_state = state->state;
438 state->state = PARSE_UNKNOWN;
439 state->unknown_depth++;
440 }
441 break;
442
443 case PARSE_DC_SETTINGS:
444 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:enabled")) {
445 state->state = PARSE_DC_ENABLED;
446 g_string_truncate (state->content, 0);
447 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vsrc1")) {
448 state->state = PARSE_DC_VSRC;
449 g_string_truncate (state->content, 0);
450 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout1")) {
451 state->state = PARSE_DC_VOUT;
452 g_string_truncate (state->content, 0);
453 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:start1")) {
454 state->state = PARSE_DC_START;
455 g_string_truncate (state->content, 0);
456 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:stop1")) {
457 state->state = PARSE_DC_STOP;
458 g_string_truncate (state->content, 0);
459 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:step1")) {
460 state->state = PARSE_DC_STEP;
461 g_string_truncate (state->content, 0);
462 } else {
463 state->prev_state = state->state;
464 state->state = PARSE_UNKNOWN;
465 state->unknown_depth++;
466 }
467 break;
468
469 case PARSE_FOURIER_SETTINGS:
470 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:enabled")) {
471 state->state = PARSE_FOURIER_ENABLED;
472 g_string_truncate (state->content, 0);
473 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:freq")) {
474 state->state = PARSE_FOURIER_FREQ;
475 g_string_truncate (state->content, 0);
476 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout")) {
477 state->state = PARSE_FOURIER_VOUT;
478 g_string_truncate (state->content, 0);
479 } else {
480 state->prev_state = state->state;
481 state->state = PARSE_UNKNOWN;
482 state->unknown_depth++;
483 }
484 break;
485
486 case PARSE_NOISE_SETTINGS:
487 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:enabled")) {
488 state->state = PARSE_NOISE_ENABLED;
489 g_string_truncate (state->content, 0);
490 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vsrc1")) {
491 state->state = PARSE_NOISE_VSRC;
492 g_string_truncate (state->content, 0);
493 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout1")) {
494 state->state = PARSE_NOISE_VOUT;
495 g_string_truncate (state->content, 0);
496 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:type")) {
497 state->state = PARSE_NOISE_TYPE;
498 g_string_truncate (state->content, 0);
499 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:npoints")) {
500 state->state = PARSE_NOISE_NPOINTS;
501 g_string_truncate (state->content, 0);
502 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:start")) {
503 state->state = PARSE_NOISE_START;
504 g_string_truncate (state->content, 0);
505 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:stop")) {
506 state->state = PARSE_NOISE_STOP;
507 g_string_truncate (state->content, 0);
508 } else {
509 state->prev_state = state->state;
510 state->state = PARSE_UNKNOWN;
511 state->unknown_depth++;
512 }
513 break;
514
515 case PARSE_OPTION_LIST:
516 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:option")) {
517 state->state = PARSE_OPTION;
518 state->option = g_new0 (SimOption, 1);
519 g_string_truncate (state->content, 0);
520 } else {
521 state->prev_state = state->state;
522 state->state = PARSE_UNKNOWN;
523 state->unknown_depth++;
524 }
525 break;
526 case PARSE_OPTION:
527 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:name")) {
528 state->state = PARSE_OPTION_NAME;
529 g_string_truncate (state->content, 0);
530 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:value")) {
531 state->state = PARSE_OPTION_VALUE;
532 g_string_truncate (state->content, 0);
533 } else {
534 state->prev_state = state->state;
535 state->state = PARSE_UNKNOWN;
536 state->unknown_depth++;
537 }
538
539 break;
540 case PARSE_PARTS:
541 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:part")) {
542 state->state = PARSE_PART;
543 state->part = g_new0 (LibraryPart, 1);
544 state->rotation = 0;
545 state->flip = ID_FLIP_NONE;
546 state->label = NULL;
547 state->property = NULL;
548 } else {
549 state->prev_state = state->state;
550 state->state = PARSE_UNKNOWN;
551 state->unknown_depth++;
552 }
553 break;
554 case PARSE_PART:
555 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:name")) {
556 state->state = PARSE_PART_NAME;
557 g_string_truncate (state->content, 0);
558 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:library")) {
559 state->state = PARSE_PART_LIBNAME;
560 g_string_truncate (state->content, 0);
561 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:refdes")) {
562 state->state = PARSE_PART_REFDES;
563 g_string_truncate (state->content, 0);
564 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:position")) {
565 state->state = PARSE_PART_POSITION;
566 g_string_truncate (state->content, 0);
567 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:rotation")) {
568 state->state = PARSE_PART_ROTATION;
569 g_string_truncate (state->content, 0);
570 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:flip")) {
571 state->state = PARSE_PART_FLIP;
572 g_string_truncate (state->content, 0);
573 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:template")) {
574 state->state = PARSE_PART_TEMPLATE;
575 g_string_truncate (state->content, 0);
576 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:model")) {
577 state->state = PARSE_PART_MODEL;
578 g_string_truncate (state->content, 0);
579 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:symbol")) {
580 state->state = PARSE_PART_SYMNAME;
581 g_string_truncate (state->content, 0);
582 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:labels")) {
583 state->state = PARSE_PART_LABELS;
584 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:properties")) {
585 state->state = PARSE_PART_PROPERTIES;
586 } else {
587 state->prev_state = state->state;
588 state->state = PARSE_UNKNOWN;
589 state->unknown_depth++;
590 }
591 break;
592 case PARSE_PART_LABELS:
593 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:label")) {
594 state->state = PARSE_PART_LABEL;
595 state->label = g_new0 (PartLabel, 1);
596 } else {
597 state->prev_state = state->state;
598 state->state = PARSE_UNKNOWN;
599 state->unknown_depth++;
600 }
601 break;
602 case PARSE_PART_LABEL:
603 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:name")) {
604 state->state = PARSE_PART_LABEL_NAME;
605 g_string_truncate (state->content, 0);
606 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:text")) {
607 state->state = PARSE_PART_LABEL_TEXT;
608 g_string_truncate (state->content, 0);
609 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:position")) {
610 state->state = PARSE_PART_LABEL_POS;
611 g_string_truncate (state->content, 0);
612 } else {
613 state->prev_state = state->state;
614 state->state = PARSE_UNKNOWN;
615 state->unknown_depth++;
616 }
617 break;
618
619 case PARSE_PART_PROPERTIES:
620 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:property")) {
621 state->state = PARSE_PART_PROPERTY;
622 state->property = g_new0 (Property, 1);
623 } else {
624 state->prev_state = state->state;
625 state->state = PARSE_UNKNOWN;
626 state->unknown_depth++;
627 }
628 break;
629 case PARSE_PART_PROPERTY:
630 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:name")) {
631 state->state = PARSE_PART_PROPERTY_NAME;
632 g_string_truncate (state->content, 0);
633 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:value")) {
634 state->state = PARSE_PART_PROPERTY_VALUE;
635 g_string_truncate (state->content, 0);
636 } else {
637 state->prev_state = state->state;
638 state->state = PARSE_UNKNOWN;
639 state->unknown_depth++;
640 }
641 break;
642
643 case PARSE_WIRES:
644 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:wire")) {
645 state->state = PARSE_WIRE;
646 } else {
647 state->prev_state = state->state;
648 state->state = PARSE_UNKNOWN;
649 state->unknown_depth++;
650 }
651 break;
652 case PARSE_WIRE:
653 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:points")) {
654 state->state = PARSE_WIRE_POINTS;
655 g_string_truncate (state->content, 0);
656 } else {
657 state->prev_state = state->state;
658 state->state = PARSE_UNKNOWN;
659 state->unknown_depth++;
660 }
661 break;
662
663 case PARSE_TEXTBOXES:
664 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:textbox")) {
665 state->state = PARSE_TEXTBOX;
666 } else {
667 state->prev_state = state->state;
668 state->state = PARSE_UNKNOWN;
669 state->unknown_depth++;
670 }
671 break;
672 case PARSE_TEXTBOX:
673 if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:position")) {
674 state->state = PARSE_TEXTBOX_POSITION;
675 g_string_truncate (state->content, 0);
676 } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:text")) {
677 state->state = PARSE_TEXTBOX_TEXT;
678 g_string_truncate (state->content, 0);
679 } else {
680 state->prev_state = state->state;
681 state->state = PARSE_UNKNOWN;
682 state->unknown_depth++;
683 }
684 break;
685
686 case PARSE_TRANSIENT_ENABLED:
687 case PARSE_TRANSIENT_START:
688 case PARSE_TRANSIENT_STOP:
689 case PARSE_TRANSIENT_STEP:
690 case PARSE_TRANSIENT_STEP_ENABLE:
691
692 case PARSE_AC_ENABLED:
693 case PARSE_AC_VOUT:
694 case PARSE_AC_TYPE:
695 case PARSE_AC_NPOINTS:
696 case PARSE_AC_START:
697 case PARSE_AC_STOP:
698
699 case PARSE_DC_ENABLED:
700 case PARSE_DC_VSRC:
701 case PARSE_DC_VOUT:
702 case PARSE_DC_START:
703 case PARSE_DC_STOP:
704 case PARSE_DC_STEP:
705
706 case PARSE_FOURIER_ENABLED:
707 case PARSE_FOURIER_FREQ:
708 case PARSE_FOURIER_VOUT:
709
710 case PARSE_NOISE_ENABLED:
711 case PARSE_NOISE_VSRC:
712 case PARSE_NOISE_VOUT:
713 case PARSE_NOISE_TYPE:
714 case PARSE_NOISE_NPOINTS:
715 case PARSE_NOISE_START:
716 case PARSE_NOISE_STOP:
717
718 case PARSE_WIRE_POINTS:
719 case PARSE_OPTION_NAME:
720 case PARSE_OPTION_VALUE:
721 case PARSE_PART_NAME:
722 case PARSE_PART_LIBNAME:
723 case PARSE_PART_TEMPLATE:
724 case PARSE_PART_MODEL:
725 case PARSE_PART_REFDES:
726 case PARSE_PART_POSITION:
727 case PARSE_PART_ROTATION:
728 case PARSE_PART_FLIP:
729 case PARSE_PART_SYMNAME:
730 case PARSE_PART_LABEL_NAME:
731 case PARSE_PART_LABEL_TEXT:
732 case PARSE_PART_LABEL_POS:
733 case PARSE_PART_PROPERTY_NAME:
734 case PARSE_PART_PROPERTY_VALUE:
735 case PARSE_TEXTBOX_POSITION:
736 case PARSE_TEXTBOX_TEXT:
737 case PARSE_ZOOM:
738 case PARSE_TITLE:
739 case PARSE_AUTHOR:
740 case PARSE_VERSION:
741 case PARSE_COMMENTS:
742 case PARSE_TRANSIENT_INIT_COND:
743 case PARSE_TRANSIENT_ANALYZE_ALL:
744 // there should be no tags inside these types of tags
745 g_message ("*** '%s' tag found", name);
746 state->prev_state = state->state;
747 state->state = PARSE_UNKNOWN;
748 state->unknown_depth++;
749 break;
750
751 case PARSE_ERROR:
752 break;
753 case PARSE_UNKNOWN:
754 state->unknown_depth++;
755 break;
756 case PARSE_FINISH:
757 // should not start new elements in this state
758 g_assert_not_reached ();
759 break;
760 }
761 // g_message("Start element %s (state %s)", name, states[state->state]);
762 }
763
end_element(ParseState * state,const xmlChar * name)764 static void end_element (ParseState *state, const xmlChar *name)
765 {
766 GList *libs;
767 Schematic *schematic = state->schematic;
768
769 switch (state->state) {
770 case PARSE_UNKNOWN:
771 state->unknown_depth--;
772 if (state->unknown_depth == 0)
773 state->state = state->prev_state;
774 break;
775 case PARSE_AUTHOR:
776 state->author = g_strdup (state->content->str);
777 state->state = PARSE_SCHEMATIC;
778 break;
779 case PARSE_TITLE:
780 state->title = g_strdup (state->content->str);
781 state->state = PARSE_SCHEMATIC;
782 break;
783 case PARSE_VERSION:
784 state->oregano_version = g_strdup (state->content->str);
785 state->state = PARSE_SCHEMATIC;
786 break;
787 case PARSE_COMMENTS:
788 state->comments = g_strdup (state->content->str);
789 state->state = PARSE_SCHEMATIC;
790 break;
791 case PARSE_ZOOM: {
792 double zoom;
793
794 zoom = g_strtod (state->content->str, NULL);
795 schematic_set_zoom (state->schematic, zoom);
796 state->state = PARSE_SCHEMATIC;
797
798 break;
799 }
800
801 case PARSE_SIMULATION_SETTINGS:
802 state->state = PARSE_SCHEMATIC;
803 break;
804
805 case PARSE_TRANSIENT_SETTINGS:
806 state->state = PARSE_SIMULATION_SETTINGS;
807 break;
808 case PARSE_TRANSIENT_ENABLED:
809 sim_settings_set_trans (state->sim_settings,
810 !g_ascii_strcasecmp (state->content->str, "true"));
811 state->state = PARSE_TRANSIENT_SETTINGS;
812 break;
813 case PARSE_TRANSIENT_START:
814 sim_settings_set_trans_start (state->sim_settings, state->content->str);
815 state->state = PARSE_TRANSIENT_SETTINGS;
816 break;
817 case PARSE_TRANSIENT_STOP:
818 sim_settings_set_trans_stop (state->sim_settings, state->content->str);
819 state->state = PARSE_TRANSIENT_SETTINGS;
820 break;
821 case PARSE_TRANSIENT_STEP:
822 sim_settings_set_trans_step (state->sim_settings, state->content->str);
823 state->state = PARSE_TRANSIENT_SETTINGS;
824 break;
825 case PARSE_TRANSIENT_STEP_ENABLE:
826 sim_settings_set_trans_step_enable (state->sim_settings,
827 !g_ascii_strcasecmp (state->content->str, "true"));
828 state->state = PARSE_TRANSIENT_SETTINGS;
829 break;
830 case PARSE_TRANSIENT_INIT_COND:
831 sim_settings_set_trans_init_cond (state->sim_settings,
832 !g_ascii_strcasecmp (state->content->str, "true"));
833 state->state = PARSE_TRANSIENT_SETTINGS;
834 break;
835 case PARSE_TRANSIENT_ANALYZE_ALL:
836 sim_settings_set_trans_analyze_all(state->sim_settings,
837 !g_ascii_strcasecmp (state->content->str, "true"));
838 state->state = PARSE_TRANSIENT_SETTINGS;
839 break;
840
841 case PARSE_AC_SETTINGS:
842 state->state = PARSE_SIMULATION_SETTINGS;
843 break;
844 case PARSE_AC_ENABLED:
845 sim_settings_set_ac (state->sim_settings,
846 !g_ascii_strcasecmp (state->content->str, "true"));
847 state->state = PARSE_AC_SETTINGS;
848 break;
849 case PARSE_AC_VOUT:
850 sim_settings_set_ac_vout (state->sim_settings, state->content->str);
851 state->state = PARSE_AC_SETTINGS;
852 break;
853 case PARSE_AC_TYPE:
854 sim_settings_set_ac_type (state->sim_settings, state->content->str);
855 state->state = PARSE_AC_SETTINGS;
856 break;
857 case PARSE_AC_NPOINTS:
858 sim_settings_set_ac_npoints (state->sim_settings, state->content->str);
859 state->state = PARSE_AC_SETTINGS;
860 break;
861 case PARSE_AC_START:
862 sim_settings_set_ac_start (state->sim_settings, state->content->str);
863 state->state = PARSE_AC_SETTINGS;
864 break;
865 case PARSE_AC_STOP:
866 sim_settings_set_ac_stop (state->sim_settings, state->content->str);
867 state->state = PARSE_AC_SETTINGS;
868 break;
869
870 case PARSE_DC_SETTINGS:
871 state->state = PARSE_SIMULATION_SETTINGS;
872 break;
873 case PARSE_DC_ENABLED:
874 sim_settings_set_dc (state->sim_settings,
875 !g_ascii_strcasecmp (state->content->str, "true"));
876 state->state = PARSE_DC_SETTINGS;
877 break;
878 case PARSE_DC_VSRC:
879 sim_settings_set_dc_vsrc (state->sim_settings, state->content->str);
880 state->state = PARSE_DC_SETTINGS;
881 break;
882 case PARSE_DC_VOUT:
883 sim_settings_set_dc_vout (state->sim_settings, state->content->str);
884 state->state = PARSE_DC_SETTINGS;
885 break;
886 case PARSE_DC_START:
887 sim_settings_set_dc_start (state->sim_settings, state->content->str);
888 state->state = PARSE_DC_SETTINGS;
889 break;
890 case PARSE_DC_STOP:
891 sim_settings_set_dc_stop (state->sim_settings, state->content->str);
892 state->state = PARSE_DC_SETTINGS;
893 break;
894 case PARSE_DC_STEP:
895 sim_settings_set_dc_step (state->sim_settings, state->content->str);
896 state->state = PARSE_DC_SETTINGS;
897 break;
898
899 case PARSE_FOURIER_SETTINGS:
900 state->state = PARSE_SIMULATION_SETTINGS;
901 break;
902 case PARSE_FOURIER_ENABLED:
903 sim_settings_set_fourier (state->sim_settings,
904 !g_ascii_strcasecmp (state->content->str, "true"));
905 state->state = PARSE_FOURIER_SETTINGS;
906 break;
907 case PARSE_FOURIER_FREQ:
908 sim_settings_set_fourier_frequency (state->sim_settings, state->content->str);
909 state->state = PARSE_FOURIER_SETTINGS;
910 break;
911 case PARSE_FOURIER_VOUT:
912 sim_settings_set_fourier_vout (state->sim_settings, state->content->str);
913 state->state = PARSE_FOURIER_SETTINGS;
914 break;
915
916 case PARSE_NOISE_SETTINGS:
917 state->state = PARSE_SIMULATION_SETTINGS;
918 break;
919 case PARSE_NOISE_ENABLED:
920 sim_settings_set_noise (state->sim_settings,
921 !g_ascii_strcasecmp (state->content->str, "true"));
922 state->state = PARSE_NOISE_SETTINGS;
923 break;
924 case PARSE_NOISE_VSRC:
925 sim_settings_set_noise_vsrc (state->sim_settings, state->content->str);
926 state->state = PARSE_NOISE_SETTINGS;
927 break;
928 case PARSE_NOISE_VOUT:
929 sim_settings_set_noise_vout (state->sim_settings, state->content->str);
930 state->state = PARSE_NOISE_SETTINGS;
931 break;
932 case PARSE_NOISE_TYPE:
933 sim_settings_set_noise_type (state->sim_settings, state->content->str);
934 state->state = PARSE_NOISE_SETTINGS;
935 break;
936 case PARSE_NOISE_NPOINTS:
937 sim_settings_set_noise_npoints (state->sim_settings, state->content->str);
938 state->state = PARSE_NOISE_SETTINGS;
939 break;
940 case PARSE_NOISE_START:
941 sim_settings_set_noise_start (state->sim_settings, state->content->str);
942 state->state = PARSE_NOISE_SETTINGS;
943 break;
944 case PARSE_NOISE_STOP:
945 sim_settings_set_noise_stop (state->sim_settings, state->content->str);
946 state->state = PARSE_NOISE_SETTINGS;
947 break;
948
949 case PARSE_OPTION_LIST:
950 state->state = PARSE_SIMULATION_SETTINGS;
951 break;
952 case PARSE_OPTION:
953 state->state = PARSE_OPTION_LIST;
954 sim_settings_add_option (state->sim_settings, state->option);
955 state->option = g_new0 (SimOption, 1);
956 break;
957 case PARSE_OPTION_NAME:
958 state->option->name = g_strdup (state->content->str);
959 state->state = PARSE_OPTION;
960 break;
961
962 case PARSE_OPTION_VALUE:
963 state->option->value = g_strdup (state->content->str);
964 state->state = PARSE_OPTION;
965 break;
966
967 case PARSE_PARTS:
968 state->state = PARSE_SCHEMATIC;
969 break;
970 case PARSE_PART:
971 create_part (state);
972 state->state = PARSE_PARTS;
973 break;
974 case PARSE_PART_NAME:
975 state->part->name = g_strdup (state->content->str);
976 state->state = PARSE_PART;
977 break;
978 case PARSE_PART_LIBNAME:
979 state->state = PARSE_PART;
980 state->part->library = NULL;
981 libs = oregano.libraries;
982 while (libs) {
983 Library *lib = (Library *)libs->data;
984 if (g_ascii_strcasecmp (state->content->str, lib->name) == 0) {
985 state->part->library = lib;
986 break;
987 }
988 libs = libs->next;
989 }
990 break;
991 case PARSE_PART_POSITION:
992 sscanf (state->content->str, "(%lf %lf)", &state->pos.x, &state->pos.y);
993 // Try to fix invalid positions
994 if (state->pos.x < 0)
995 state->pos.x = -state->pos.x;
996 if (state->pos.y < 0)
997 state->pos.y = -state->pos.y;
998 // Determine the maximum parts' coordinates to be used during sheet creation
999 if (state->pos.x > schematic_get_width(schematic))
1000 schematic_set_width(schematic, (guint) state->pos.x);
1001 if (state->pos.y > schematic_get_height(schematic))
1002 schematic_set_height(schematic, (guint) state->pos.y);
1003 state->state = PARSE_PART;
1004 break;
1005 case PARSE_PART_ROTATION:
1006 sscanf (state->content->str, "%d", &state->rotation);
1007 state->state = PARSE_PART;
1008 break;
1009 case PARSE_PART_FLIP:
1010 if (g_ascii_strcasecmp (state->content->str, "horizontal") == 0)
1011 state->flip = state->flip | ID_FLIP_HORIZ;
1012 else if (g_ascii_strcasecmp (state->content->str, "vertical") == 0)
1013 state->flip = state->flip | ID_FLIP_VERT;
1014 state->state = PARSE_PART;
1015 break;
1016 case PARSE_PART_REFDES:
1017 state->part->refdes = g_strdup (state->content->str);
1018 state->state = PARSE_PART;
1019 break;
1020 case PARSE_PART_TEMPLATE:
1021 state->part->template = g_strdup (state->content->str);
1022 state->state = PARSE_PART;
1023 break;
1024 case PARSE_PART_MODEL:
1025 state->part->model = g_strdup (state->content->str);
1026 state->state = PARSE_PART;
1027 break;
1028 case PARSE_PART_SYMNAME:
1029 state->part->symbol_name = g_strdup (state->content->str);
1030 state->state = PARSE_PART;
1031 break;
1032 case PARSE_PART_LABELS:
1033 state->state = PARSE_PART;
1034 break;
1035 case PARSE_PART_LABEL:
1036 state->state = PARSE_PART_LABELS;
1037 state->part->labels = g_slist_prepend (state->part->labels, state->label);
1038 break;
1039 case PARSE_PART_LABEL_NAME:
1040 state->label->name = g_strdup (state->content->str);
1041 state->state = PARSE_PART_LABEL;
1042 break;
1043 case PARSE_PART_LABEL_TEXT:
1044 state->label->text = g_strdup (state->content->str);
1045 state->state = PARSE_PART_LABEL;
1046 break;
1047 case PARSE_PART_LABEL_POS:
1048 sscanf (state->content->str, "(%lf %lf)", &state->label->pos.x, &state->label->pos.y);
1049 state->state = PARSE_PART_LABEL;
1050 break;
1051 case PARSE_PART_PROPERTIES:
1052 state->state = PARSE_PART;
1053 break;
1054 case PARSE_PART_PROPERTY:
1055 state->state = PARSE_PART_PROPERTIES;
1056 state->part->properties = g_slist_prepend (state->part->properties, state->property);
1057 break;
1058 case PARSE_PART_PROPERTY_NAME:
1059 state->property->name = g_strdup (state->content->str);
1060 state->state = PARSE_PART_PROPERTY;
1061 break;
1062 case PARSE_PART_PROPERTY_VALUE:
1063 state->property->value = g_strdup (state->content->str);
1064 state->state = PARSE_PART_PROPERTY;
1065 break;
1066
1067 case PARSE_WIRES:
1068 state->state = PARSE_SCHEMATIC;
1069 break;
1070 case PARSE_WIRE:
1071 state->state = PARSE_WIRES;
1072 break;
1073 case PARSE_WIRE_POINTS:
1074 sscanf (state->content->str, "(%lf %lf)(%lf %lf)", &state->wire_start.x,
1075 &state->wire_start.y, &state->wire_end.x, &state->wire_end.y);
1076 create_wire (state);
1077 state->state = PARSE_WIRE;
1078 break;
1079
1080 case PARSE_TEXTBOXES:
1081 state->state = PARSE_SCHEMATIC;
1082 break;
1083 case PARSE_TEXTBOX:
1084 create_textbox (state);
1085 state->state = PARSE_TEXTBOXES;
1086 break;
1087 case PARSE_TEXTBOX_POSITION:
1088 sscanf (state->content->str, "(%lf %lf)", &state->pos.x, &state->pos.y);
1089 state->state = PARSE_TEXTBOX;
1090 break;
1091 case PARSE_TEXTBOX_TEXT:
1092 state->textbox_text = g_strdup (state->content->str);
1093 state->state = PARSE_TEXTBOX;
1094 break;
1095 case PARSE_SCHEMATIC:
1096 // The end of the file.
1097 state->state = PARSE_FINISH;
1098 break;
1099 case PARSE_ERROR:
1100 break;
1101 case PARSE_START:
1102 case PARSE_FINISH:
1103 // There should not be a closing tag in this state.
1104 g_assert_not_reached ();
1105 break;
1106 }
1107 }
1108
my_characters(ParseState * state,const xmlChar * chars,int len)1109 static void my_characters (ParseState *state, const xmlChar *chars, int len)
1110 {
1111 int i;
1112
1113 if (state->state == PARSE_FINISH || state->state == PARSE_START ||
1114 state->state == PARSE_PARTS || state->state == PARSE_PART)
1115 return;
1116
1117 for (i = 0; i < len; i++)
1118 g_string_append_c (state->content, chars[i]);
1119 }
1120
get_entity(void * user_data,const xmlChar * name)1121 static xmlEntityPtr get_entity (void *user_data, const xmlChar *name)
1122 {
1123 return xmlGetPredefinedEntity (name);
1124 }
1125
my_warning(void * user_data,const char * msg,...)1126 static void my_warning (void *user_data, const char *msg, ...)
1127 {
1128 va_list args;
1129
1130 va_start (args, msg);
1131 g_logv ("XML", G_LOG_LEVEL_WARNING, msg, args);
1132 va_end (args);
1133 }
1134
1135 // FIXME this should not be critical but forward to the oregano log buffer
my_error(void * user_data,const char * msg,...)1136 static void my_error (void *user_data, const char *msg, ...)
1137 {
1138 va_list args;
1139
1140 va_start (args, msg);
1141 g_logv ("XML", G_LOG_LEVEL_CRITICAL, msg, args);
1142 va_end (args);
1143 }
1144
my_fatal_error(void * user_data,const char * msg,...)1145 static void my_fatal_error (void *user_data, const char *msg, ...)
1146 {
1147 va_list args;
1148
1149 va_start (args, msg);
1150 g_logv ("XML", G_LOG_LEVEL_ERROR, msg, args);
1151 va_end (args);
1152 }
1153