missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / gschem_patch.c
blob8d31c44eed2263715dbc254c5d207011c99225a2
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 2015-2020 gEDA Contributors (see ChangeLog for details)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 /*! \file gschem_patch.c
21 * \brief Back-annotation from pcb-rnd.
23 * \bug Back-annotation definitively needs automated tests, but there
24 * is currently no easy way to do this.
27 #include <config.h>
28 #include "gschem.h"
30 #define NETATTRIB_DELIMITERS ",; "
32 static const gboolean debug =
33 #if DEBUG_PATCH
34 TRUE;
35 #else
36 FALSE;
37 #endif
39 static void free_patch_line (gschem_patch_line_t *line);
42 /******************************************************************************/
43 /*! \section init Initializing the patch state. */
45 static int
46 patch_parse (gschem_patch_state_t *st, FILE *f, const char *fn)
48 char *word = NULL;
49 int alloced = 0, used = 0;
50 int c, lineno = 1;
51 gschem_patch_line_t *current = NULL;
53 enum {
54 ST_INIT,
55 ST_COMMENT,
56 ST_OP,
57 ST_PRE_STR,
58 ST_STR
59 } state = ST_INIT;
61 g_assert (st->lines == NULL);
63 do {
64 enum {
65 DO_NOP,
66 DO_APPEND,
67 DO_END_OP,
68 DO_END_STR
69 } what_to_do = DO_NOP;
70 gboolean end_line = FALSE;
72 c = fgetc (f);
74 switch (state) {
75 case ST_INIT:
76 switch (c) {
77 case '#':
78 state = ST_COMMENT;
79 break;
80 case '\r':
81 case '\n':
82 case EOF:
83 case ' ':
84 case '\t':
85 break;
86 default:
87 used = 0;
88 what_to_do = DO_APPEND;
89 state = ST_OP;
91 break;
93 case ST_OP:
94 switch (c) {
95 case '#':
96 what_to_do = DO_END_OP;
97 end_line = TRUE;
98 state = ST_COMMENT;
99 break;
100 case ' ':
101 case '\t':
102 what_to_do = DO_END_OP;
103 state = ST_PRE_STR;
104 break;
105 case '\r':
106 case '\n':
107 case EOF:
108 what_to_do = DO_END_OP;
109 end_line = TRUE;
110 state = ST_INIT;
111 break;
112 default:
113 what_to_do = DO_APPEND;
115 break;
117 case ST_PRE_STR:
118 switch (c) {
119 case '#':
120 end_line = TRUE;
121 state = ST_COMMENT;
122 break;
123 case ' ':
124 case '\t':
125 break;
126 case '\r':
127 case '\n':
128 case EOF:
129 end_line = TRUE;
130 state = ST_INIT;
131 break;
132 default:
133 used = 0;
134 what_to_do = DO_APPEND;
135 state = ST_STR;
137 break;
139 case ST_STR:
140 switch (c) {
141 case '#':
142 what_to_do = DO_END_STR;
143 end_line = TRUE;
144 state = ST_COMMENT;
145 break;
146 case ' ':
147 case '\t':
148 what_to_do = DO_END_STR;
149 state = ST_PRE_STR;
150 break;
151 case '\r':
152 case '\n':
153 case EOF:
154 what_to_do = DO_END_STR;
155 end_line = TRUE;
156 state = ST_INIT;
157 break;
158 default:
159 what_to_do = DO_APPEND;
161 break;
163 case ST_COMMENT:
164 switch (c) {
165 case '\r':
166 case '\n':
167 case EOF:
168 state = ST_INIT;
169 break;
171 break;
174 switch (what_to_do) {
175 case DO_NOP:
176 break;
178 case DO_APPEND:
179 if (used >= alloced) {
180 alloced += 64;
181 word = realloc (word, alloced);
183 word[used] = c;
184 used++;
185 break;
187 case DO_END_OP:
188 if (used >= alloced) {
189 alloced += 64;
190 word = realloc (word, alloced);
192 word[used] = '\0';
193 used++;
195 if (current != NULL) {
196 fprintf (stderr, "%s:%d: Internal error\n", fn, lineno);
197 goto error;
199 current = g_slice_new0 (gschem_patch_line_t);
201 if (strcmp (word, "add_conn") == 0)
202 current->op = GSCHEM_PATCH_ADD_CONN;
203 else if (strcmp (word, "del_conn") == 0)
204 current->op = GSCHEM_PATCH_DEL_CONN;
205 else if (strcmp (word, "change_attrib") == 0)
206 current->op = GSCHEM_PATCH_CHANGE_ATTRIB;
207 else if (strcmp (word, "net_info") == 0)
208 current->op = GSCHEM_PATCH_NET_INFO;
209 else {
210 fprintf (stderr, "%s:%d: Syntax error: unknown opcode `%s'\n",
211 fn, lineno, word);
212 goto error;
214 used = 0;
215 break;
217 case DO_END_STR:
218 if (used >= alloced) {
219 alloced += 64;
220 word = realloc (word, alloced);
222 word[used] = '\0';
223 used++;
225 if (*word != '\0') {
226 if (current == NULL) {
227 fprintf (stderr, "%s:%d: Internal error\n", fn, lineno);
228 goto error;
230 switch (current->op) {
231 case GSCHEM_PATCH_DEL_CONN:
232 case GSCHEM_PATCH_ADD_CONN:
233 if (current->id == NULL)
234 current->id = strdup (word);
235 else if (current->arg1.net_name == NULL)
236 current->arg1.net_name = strdup (word);
237 else {
238 fprintf (stderr, "%s:%d: Need two arguments for the "
239 "connection: netname and pinname\n",
240 fn, lineno);
241 goto error;
243 break;
244 case GSCHEM_PATCH_CHANGE_ATTRIB:
245 if (current->id == NULL)
246 current->id = strdup (word);
247 else if (current->arg1.attrib_name == NULL)
248 current->arg1.attrib_name = strdup (word);
249 else if (current->arg2.attrib_val == NULL)
250 current->arg2.attrib_val = strdup (word);
251 else {
252 fprintf (stderr, "%s:%d: Need three arguments for an "
253 "attrib change: id attr_name attr_val\n",
254 fn, lineno);
255 goto error;
257 break;
258 case GSCHEM_PATCH_NET_INFO:
259 if (current->id == NULL)
260 current->id = strdup (word);
261 else
262 current->arg1.ids =
263 g_list_prepend (current->arg1.ids, strdup (word));
264 break;
267 used = 0;
268 break;
271 if (end_line) {
272 if (current == NULL) {
273 fprintf (stderr, "%s:%d: Internal error\n", fn, lineno);
274 goto error;
276 switch (current->op) {
277 case GSCHEM_PATCH_DEL_CONN:
278 case GSCHEM_PATCH_ADD_CONN:
279 if (current->id == NULL ||
280 current->arg1.net_name == NULL) {
281 fprintf (stderr, "%s:%d: Not enough arguments\n", fn, lineno);
282 goto error;
284 break;
285 case GSCHEM_PATCH_CHANGE_ATTRIB:
286 if (current->id == NULL ||
287 current->arg1.attrib_name == NULL ||
288 current->arg2.attrib_val == NULL) {
289 fprintf (stderr, "%s:%d: Not enough arguments\n", fn, lineno);
290 goto error;
292 break;
293 case GSCHEM_PATCH_NET_INFO:
294 if (current->id == NULL) {
295 fprintf (stderr, "%s:%d: Not enough arguments\n", fn, lineno);
296 goto error;
298 break;
300 st->lines = g_list_prepend (st->lines, current);
301 current = NULL;
302 used = 0;
305 if (c == '\n')
306 lineno++;
307 } while (c != EOF);
309 st->lines = g_list_reverse (st->lines);
310 if (current != NULL)
311 free_patch_line (current);
312 free (word);
313 return 0;
315 error:
316 g_list_free_full (st->lines, (GDestroyNotify) free_patch_line);
317 if (current != NULL)
318 free_patch_line (current);
319 free (word);
320 return -1;
324 static void
325 debug_print_lines (gschem_patch_state_t *st)
327 static const char *op_names[] = {
328 "disconnect",
329 "connect",
330 "chnage attribute",
331 "net_info"
334 for (GList *i = st->lines; i != NULL; i = i->next) {
335 gschem_patch_line_t *l = i->data;
336 if (l == NULL) {
337 fprintf (stderr, "NULL data on list\n");
338 continue;
340 switch (l->op) {
341 case GSCHEM_PATCH_DEL_CONN:
342 case GSCHEM_PATCH_ADD_CONN:
343 fprintf (stderr, "%s %s %s\n", op_names[l->op], l->id,
344 l->arg1.net_name);
345 break;
346 case GSCHEM_PATCH_CHANGE_ATTRIB:
347 fprintf (stderr, "%s %s %s=%s\n", op_names[l->op], l->id,
348 l->arg1.attrib_name, l->arg2.attrib_val);
349 break;
350 case GSCHEM_PATCH_NET_INFO:
351 fprintf (stderr, "%s %s", op_names[l->op], l->id);
352 for (GList *p = l->arg1.ids; p != NULL; p = p->next)
353 fprintf (stderr, " %s", (char *) p->data);
354 fprintf (stderr, "\n");
355 break;
360 /*! \brief Initialize a patch state struct and read a patch file.
362 * If reading the patch file fails, \c -1 is returned and \a st is
363 * left uninitialized.
365 * \returns \c 0 on success, \c -1 on failure
368 gschem_patch_state_init (gschem_patch_state_t *st, const char *fn)
370 FILE *f;
371 int res;
373 memset (st, 0, sizeof *st);
375 f = fopen (fn, "r");
376 if (f == NULL)
377 return -1;
379 res = patch_parse (st, f, fn);
381 if (debug)
382 debug_print_lines (st);
384 if (res == 0) {
385 /* Create hashes for faster lookups avoiding O(objects*patches) */
386 st->pins = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
387 st->comps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
388 st->nets = g_hash_table_new (g_str_hash, g_str_equal);
389 for (GList *i = st->lines; i != NULL; i = i->next) {
390 gschem_patch_line_t *l = i->data;
391 if (l->op == GSCHEM_PATCH_NET_INFO) {
392 g_hash_table_insert (st->nets, l->id, l->arg1.ids);
393 l->arg1.ids = NULL; /* transfer ownership */
398 fclose (f);
399 return res;
403 /******************************************************************************/
404 /*! \section build Populating the patch state hashes. */
407 /* insert item in a hash table of slists */
408 static void
409 build_insert_hash_list (GHashTable *hash, char *full_name, void *item)
411 GSList *lst = g_hash_table_lookup (hash, full_name);
412 g_hash_table_insert (hash, full_name, g_slist_prepend (lst, item));
415 static gschem_patch_pin_t *
416 alloc_pin (OBJECT *pin_obj, char *net)
418 gschem_patch_pin_t *p = g_slice_new (gschem_patch_pin_t);
419 p->obj = pin_obj;
420 p->net = net;
421 return p;
424 /*! \brief Make an object known to a patch state.
426 * \returns \c 0
429 gschem_patch_state_build (gschem_patch_state_t *st, OBJECT *o)
431 GList *l;
432 gchar *refdes, *pin;
434 switch (o->type) {
435 case OBJ_COMPLEX:
436 refdes = o_attrib_search_object_attribs_by_name (o, "refdes", 0);
437 if (refdes == NULL)
438 break;
440 /* map the component */
441 build_insert_hash_list (st->comps, g_strdup (refdes), o);
443 /* map pins */
444 for (GList *i = o->complex->prim_objs; i != NULL; i = i->next) {
445 OBJECT *sub = i->data;
446 switch (sub->type) {
447 case OBJ_PIN:
448 pin = o_attrib_search_object_attribs_by_name (sub, "pinnumber", 0);
449 if (pin != NULL) {
450 char *full_name;
451 full_name = g_strdup_printf ("%s-%s", refdes, pin);
452 //printf ("add: '%s' -> '%p' o=%p at=%p p=%p\n",
453 // full_name, sub, o, sub->attached_to, sub->parent);
454 //fflush (stdout);
455 build_insert_hash_list (st->pins, full_name,
456 alloc_pin (sub, NULL));
457 g_free (pin);
459 break;
463 /* map net attribute connections */
464 l = o_attrib_return_attribs (o);
465 for (GList *i = l; i != NULL; i = i->next) {
466 OBJECT *attrib = i->data;
467 /* I know, I know, I should use o_attrib_get_name_value(), but it'd be
468 ridicolous to get everything strdup'd */
469 if (attrib->type != OBJ_TEXT)
470 continue;
472 if (strncmp (attrib->text->string, "net=", 4) == 0) {
473 char *net = attrib->text->string + 4;
474 char *pinlist = strchr (net, ':');
475 char *full_name;
476 if (pinlist != NULL) {
477 int net_len;
478 char *net_name = NULL;
480 net_len = pinlist - net;
481 if (net_len > 0)
482 net_name = g_strndup (net, net_len);
483 else
484 net_name = NULL;
486 /* create a copy of the pin list on which to run strtok_r(3) */
487 pinlist = g_strdup(pinlist + 1);
489 char *pinno, *saveptr = NULL;
490 for (pinno = strtok_r (pinlist, NETATTRIB_DELIMITERS, &saveptr);
491 pinno != NULL;
492 pinno = strtok_r (NULL, NETATTRIB_DELIMITERS, &saveptr)) {
493 full_name = g_strdup_printf ("%s-%s", refdes, pinno);
494 //printf ("add: '%s' -> '%p';'%s'\n", full_name, o, net_name);
495 //fflush (stdout);
496 build_insert_hash_list (st->pins, full_name,
497 alloc_pin (o, g_strdup (net_name)));
500 g_free (pinlist);
501 g_free (net_name);
505 g_list_free (l);
507 /* clean up */
508 g_free (refdes);
509 break;
511 /* what to do with nets? */
512 case OBJ_NET:
513 //printf ("type: '%c'\n", o->type);
514 break;
516 /* ignore floating pins */
517 case OBJ_PIN:
518 break;
520 /* ignore all graphical objects */
521 case OBJ_TEXT:
522 case OBJ_LINE:
523 case OBJ_PATH:
524 case OBJ_BOX:
525 case OBJ_CIRCLE:
526 case OBJ_PICTURE:
527 case OBJ_BUS:
528 case OBJ_ARC:
529 break;
531 return 0;
535 /******************************************************************************/
536 /*! \section execute Executing the patch state. */
539 static gboolean
540 free_key (gpointer key, gpointer value, gpointer user_data)
542 free (key);
543 return TRUE;
546 static gschem_patch_hit_t *
547 alloc_hit (OBJECT *obj, gchar *loc_name, gchar *action)
549 gschem_patch_hit_t *hit = g_slice_new (gschem_patch_hit_t);
550 hit->object = obj;
551 hit->loc_name = loc_name;
552 hit->action = action;
553 return hit;
556 /*! \brief Get a list of all objects connected to this one (recursively).
558 * Gets an open list of objects to be checked and maps all connections
559 * of all objects on the list. The resulting new open list is empty,
560 * while the found hash is non-empty. For each new object on the
561 * found hash, the value is determined by calling the user provided
562 * hashval() callback.
564 * \param [in/out] found (OBJECT *) -> (value) hash of all objects found
565 * \param [in] open GList of OBJECT's to start the sarch from
566 * \param [in] hashval() a callback that generates the value of the object;
567 * all object values are NULL if hashval() is NULL
568 * \param [in] user_ctx user context pointer for hashval()
570 * \returns the new open list (empty list)
572 * \warning The caller must \c g_list_free the returned GList pointer.
573 * Also free the found hash.
575 static GList *
576 s_conn_find_all (GHashTable *found, GList *open,
577 void *(*hashval) (void *user_ctx, OBJECT *o), void *user_ctx)
579 /* iterate by consuming the first element of the list */
580 for (GList *i = open; i != NULL; i = open) {
581 OBJECT *o = i->data;
583 open = g_list_remove (open, o);
585 /* ... check if it's not yet found */
586 if (g_hash_table_lookup (found, o) == NULL) {
587 void *val;
588 if (hashval != NULL)
589 val = hashval (user_ctx, o);
590 else
591 val = NULL;
592 g_hash_table_insert (found, o, val);
593 open = s_conn_return_others (open, o);
597 return open;
600 /* return the name of the object and add relevant objects to a
601 name->obj hash in user_ctx */
602 static void *
603 exec_check_conn_hashval (void *user_ctx, OBJECT *o)
605 gchar *name = NULL, *tmp;
606 GHashTable *name2obj = user_ctx;
608 switch (o->type) {
609 case OBJ_NET:
610 tmp = o_attrib_search_object_attribs_by_name (o, "netname", 0);
611 if (tmp != NULL) {
612 name = g_strdup_printf ("%c%s", OBJ_NET, tmp);
613 g_free (tmp);
614 g_hash_table_insert (name2obj, name, o);
615 } else
616 name = " "; /* anon net segments are not interesting at all;
617 should be a static string as it doesn't end up
618 on name2obj where we free these strings */
619 break;
621 case OBJ_PIN:
622 if (o->parent != NULL) {
623 gchar *oname, *pname;
625 oname = o_attrib_search_object_attribs_by_name (o->parent, "refdes", 0);
626 if (oname == NULL)
627 break;
628 pname = o_attrib_search_object_attribs_by_name (o, "pinnumber", 0);
629 name = g_strdup_printf ("%c%s-%s", OBJ_PIN, (char *) oname,
630 (char *) pname);
631 g_free (oname);
632 g_free (pname);
633 g_hash_table_insert (name2obj, name, o);
635 break;
637 return name;
640 /* Build a name->object hash of everything connected to a pin */
641 static GHashTable *
642 exec_list_conns (OBJECT *pin)
644 GHashTable *connections = g_hash_table_new (g_str_hash, g_str_equal);
645 GHashTable *found = g_hash_table_new (g_direct_hash, NULL);
646 GList *open = g_list_prepend (NULL, pin);
647 open = s_conn_find_all (found, open, exec_check_conn_hashval, connections);
649 g_hash_table_destroy (found);
650 g_list_free (open);
651 return connections;
654 static void
655 exec_free_conns (GHashTable *connections)
657 g_hash_table_foreach_remove (connections, free_key, NULL);
658 g_hash_table_destroy (connections);
661 static void
662 exec_debug_print_conns (GHashTable *connections)
664 gpointer key, val;
665 GHashTableIter cni;
667 for (g_hash_table_iter_init (&cni, connections);
668 g_hash_table_iter_next (&cni, &key, &val); )
669 printf (" cn=%s %p\n", (char *) key, val);
672 static void
673 exec_conn_pretend (gschem_patch_line_t *patch, GList **net, int del)
675 if (del) {
676 for (GList *np = *net; np != NULL; ) {
677 char *lname = np->data;
678 np = np->next;
679 if (strcmp (lname, patch->id) == 0) {
680 *net = g_list_remove (*net, lname);
681 g_free (lname);
684 } else
685 *net = g_list_prepend (*net, g_strdup (patch->id));
688 static GSList *
689 exec_check_conn (GSList *hits, gschem_patch_line_t *patch,
690 gschem_patch_pin_t *pin, GList **net, int del)
692 GHashTable *connections = NULL;
693 int len, offs;
694 char *buff = NULL;
695 int alloced = 0, connected;
696 GString *msg = NULL;
698 if (debug)
699 printf ("exec %d:\n", del);
701 if (pin->net == NULL) {
702 connections = exec_list_conns (pin->obj);
703 if (debug)
704 exec_debug_print_conns (connections);
706 /* check if we are connected to the network */
707 len = strlen (patch->arg1.net_name);
708 if (len + 2 > alloced) {
709 alloced = len + 2 + 256;
710 free (buff);
711 buff = malloc (alloced);
713 *buff = OBJ_NET;
714 memcpy (buff + 1, patch->arg1.net_name, len + 1);
715 connected = g_hash_table_lookup (connections, buff) != NULL;
716 offs = 1;
717 } else {
718 connected = strcmp (patch->arg1.net_name, pin->net) == 0;
719 buff = g_strdup (pin->net);
720 offs = 0;
723 /* Ugly hack: do not complain about (missing) connections to unnamed nets */
724 if (strncmp (buff + offs, "unnamed_net", 11) != 0) {
725 if (connected) {
726 if (del) {
727 gchar *tmp = g_strdup_printf (_("disconnect from net %s"), buff + offs);
728 msg = g_string_new (tmp);
729 g_free (tmp);
731 } else {
732 if (!del) {
733 gchar *tmp = g_strdup_printf (_("connect to net %s"), buff + offs);
734 msg = g_string_new (tmp);
735 g_free (tmp);
740 if (connections != NULL) {
741 /* check if we still have a connection to any of the pins */
742 GString *pin_msg = NULL;
743 int pin_count = 0;
744 for (GList *np = *net; np != NULL; np = np->next) {
745 OBJECT *target;
746 len = strlen (np->data);
747 if (len + 2 > alloced) {
748 alloced = len + 2 + 256;
749 free (buff);
750 buff = malloc (alloced);
752 *buff = OBJ_PIN;
753 memcpy (buff + 1, np->data, len + 1);
754 target = g_hash_table_lookup (connections, buff);
755 if (target == pin->obj)
756 continue;
757 if ((target != NULL && del) || (target == NULL && !del)) {
758 if (pin_msg == NULL)
759 pin_msg = g_string_new (NULL);
760 else
761 g_string_append (pin_msg, ", ");
762 g_string_append (pin_msg, buff + 1);
763 pin_count++;
766 if (pin_msg != NULL) {
767 if (msg == NULL)
768 msg = g_string_new (NULL);
769 else
770 g_string_append (msg, "; ");
771 gchar *tmp = g_strdup_printf (del ? ngettext ("disconnect from pin %s",
772 "disconnect from pins %s",
773 pin_count)
774 : ngettext ("connect to pin %s",
775 "connect to pins %s",
776 pin_count),
777 g_string_free (pin_msg, FALSE));
778 g_string_append (msg, tmp);
779 g_free (tmp);
781 exec_free_conns (connections);
784 if (buff != NULL)
785 free (buff);
787 /* pretend that the item is resolved: update patch netlists */
788 exec_conn_pretend (patch, net, del);
790 if (msg != NULL) {
791 return g_slist_prepend (hits, alloc_hit (pin->obj,
792 g_strdup (patch->id),
793 g_string_free (msg, FALSE)));
796 return hits;
799 static GSList *
800 exec_check_attrib (GSList *hits, gschem_patch_line_t *patch, OBJECT *comp)
802 gchar *attr_val =
803 o_attrib_search_object_attribs_by_name (comp, patch->arg1.attrib_name, 0);
804 if (attr_val == NULL)
805 return hits;
806 if (strcmp (attr_val, patch->arg2.attrib_val) != 0) {
807 gchar *msg = g_strdup_printf (_("change attribute \"%s\" "
808 "from \"%s\" to \"%s\""),
809 patch->arg1.attrib_name, attr_val,
810 patch->arg2.attrib_val);
811 hits = g_slist_prepend (hits, alloc_hit (comp, g_strdup (patch->id), msg));
813 g_free (attr_val);
814 return hits;
818 /*! \brief Retrieve a list of hits from a fully built patch state.
820 * \returns a singly-linked list of hits in reverse order
822 GSList *
823 gschem_patch_state_execute (gschem_patch_state_t *st)
825 GList *onet, *net;
826 GSList *pins, *comps;
827 int found, del;
828 GSList *hits = NULL;
830 for (GList *i = st->lines; i != NULL; i = i->next) {
831 gschem_patch_line_t *l = i->data;
832 if (l == NULL) {
833 fprintf (stderr, "NULL data on list\n");
834 continue;
837 switch (l->op) {
838 case GSCHEM_PATCH_DEL_CONN:
839 case GSCHEM_PATCH_ADD_CONN:
840 del = l->op == GSCHEM_PATCH_DEL_CONN;
841 net = onet = g_hash_table_lookup (st->nets, l->arg1.net_name);
842 pins = g_hash_table_lookup (st->pins, l->id);
843 if (pins == NULL) {
844 /* pin not found on open schematics */
845 gchar *not_found = g_strdup_printf (_("%s (NOT FOUND)"), l->id);
846 gchar *msg = g_strdup_printf (del ? _("disconnect from net %s")
847 : _("connect to net %s"),
848 l->arg1.net_name);
849 hits = g_slist_prepend (hits, alloc_hit (NULL, not_found, msg));
850 exec_conn_pretend (l, &net, del);
851 } else {
852 /* pin found */
853 for (; pins != NULL; pins = g_slist_next (pins))
854 hits = exec_check_conn (
855 hits, l, (gschem_patch_pin_t *) pins->data, &net, del);
858 /* executing a diff may update the list */
859 if (net != onet)
860 g_hash_table_insert (st->nets, l->arg1.net_name, net);
861 break;
863 case GSCHEM_PATCH_CHANGE_ATTRIB:
864 comps = g_hash_table_lookup (st->comps, l->id);
865 for (found = 0; comps != NULL; comps = g_slist_next (comps)) {
866 hits = exec_check_attrib (hits, l, (OBJECT *) comps->data);
867 found++;
869 if (found == 0) {
870 gchar *not_found = g_strdup_printf (_("%s (NOT FOUND)"), l->id);
871 gchar *msg = g_strdup_printf (_("change attribute \"%s\" to \"%s\""),
872 l->arg1.attrib_name,
873 l->arg2.attrib_val);
874 hits = g_slist_prepend (hits, alloc_hit (NULL, not_found, msg));
876 break;
878 case GSCHEM_PATCH_NET_INFO:
879 /* just ignore them, we've already built data structs while parsing */
880 break;
884 return g_slist_reverse (hits);
888 /******************************************************************************/
889 /*! \section destroy Freeing the patch structures. */
892 static void
893 free_patch_line (gschem_patch_line_t *line)
895 switch (line->op) {
896 case GSCHEM_PATCH_DEL_CONN:
897 case GSCHEM_PATCH_ADD_CONN:
898 g_free (line->id);
899 g_free (line->arg1.net_name);
900 break;
901 case GSCHEM_PATCH_CHANGE_ATTRIB:
902 g_free (line->id);
903 g_free (line->arg1.attrib_name);
904 g_free (line->arg2.attrib_val);
905 break;
906 case GSCHEM_PATCH_NET_INFO:
907 g_free (line->id);
908 g_list_free_full (line->arg1.ids, g_free);
909 break;
911 g_slice_free (gschem_patch_line_t, line);
914 static void
915 free_pin (gschem_patch_pin_t *pin)
917 g_free (pin->net);
918 g_slice_free (gschem_patch_pin_t, pin);
921 /*! \brief Free all memory allocated by a patch state.
923 * \note This *does not* free the patch state struct \a st itself.
925 void
926 gschem_patch_state_destroy (gschem_patch_state_t *st)
928 GHashTableIter iter;
929 gpointer key, value;
931 g_hash_table_iter_init (&iter, st->pins);
932 while (g_hash_table_iter_next (&iter, &key, &value))
933 g_slist_free_full ((GSList *) value, (GDestroyNotify) free_pin);
935 g_hash_table_iter_init (&iter, st->comps);
936 while (g_hash_table_iter_next (&iter, &key, &value))
937 g_slist_free ((GSList *) value);
939 g_hash_table_iter_init (&iter, st->nets);
940 while (g_hash_table_iter_next (&iter, &key, &value))
941 g_list_free_full ((GList *) value, g_free);
943 g_hash_table_destroy (st->nets);
944 g_hash_table_destroy (st->pins);
945 g_hash_table_destroy (st->comps);
946 g_list_free_full (st->lines, (GDestroyNotify) free_patch_line);
950 static void
951 free_hit (gschem_patch_hit_t *hit)
953 g_free (hit->loc_name);
954 g_free (hit->action);
955 g_slice_free (gschem_patch_hit_t, hit);
958 /*! \brief Free a list of hits returned by \ref gschem_patch_state_execute.
960 * \note This frees all hits in the list as well as the list itself.
962 void
963 gschem_patch_free_hit_list (GSList *hits)
965 g_slist_free_full (hits, (GDestroyNotify) free_hit);