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.
30 #define NETATTRIB_DELIMITERS ",; "
32 static const gboolean debug
=
39 static void free_patch_line (gschem_patch_line_t
*line
);
42 /******************************************************************************/
43 /*! \section init Initializing the patch state. */
46 patch_parse (gschem_patch_state_t
*st
, FILE *f
, const char *fn
)
49 int alloced
= 0, used
= 0;
51 gschem_patch_line_t
*current
= NULL
;
61 g_assert (st
->lines
== NULL
);
69 } what_to_do
= DO_NOP
;
70 gboolean end_line
= FALSE
;
88 what_to_do
= DO_APPEND
;
96 what_to_do
= DO_END_OP
;
102 what_to_do
= DO_END_OP
;
108 what_to_do
= DO_END_OP
;
113 what_to_do
= DO_APPEND
;
134 what_to_do
= DO_APPEND
;
142 what_to_do
= DO_END_STR
;
148 what_to_do
= DO_END_STR
;
154 what_to_do
= DO_END_STR
;
159 what_to_do
= DO_APPEND
;
174 switch (what_to_do
) {
179 if (used
>= alloced
) {
181 word
= realloc (word
, alloced
);
188 if (used
>= alloced
) {
190 word
= realloc (word
, alloced
);
195 if (current
!= NULL
) {
196 fprintf (stderr
, "%s:%d: Internal error\n", fn
, lineno
);
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
;
210 fprintf (stderr
, "%s:%d: Syntax error: unknown opcode `%s'\n",
218 if (used
>= alloced
) {
220 word
= realloc (word
, alloced
);
226 if (current
== NULL
) {
227 fprintf (stderr
, "%s:%d: Internal error\n", fn
, lineno
);
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
);
238 fprintf (stderr
, "%s:%d: Need two arguments for the "
239 "connection: netname and pinname\n",
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
);
252 fprintf (stderr
, "%s:%d: Need three arguments for an "
253 "attrib change: id attr_name attr_val\n",
258 case GSCHEM_PATCH_NET_INFO
:
259 if (current
->id
== NULL
)
260 current
->id
= strdup (word
);
263 g_list_prepend (current
->arg1
.ids
, strdup (word
));
272 if (current
== NULL
) {
273 fprintf (stderr
, "%s:%d: Internal error\n", fn
, lineno
);
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
);
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
);
293 case GSCHEM_PATCH_NET_INFO
:
294 if (current
->id
== NULL
) {
295 fprintf (stderr
, "%s:%d: Not enough arguments\n", fn
, lineno
);
300 st
->lines
= g_list_prepend (st
->lines
, current
);
309 st
->lines
= g_list_reverse (st
->lines
);
311 free_patch_line (current
);
316 g_list_free_full (st
->lines
, (GDestroyNotify
) free_patch_line
);
318 free_patch_line (current
);
325 debug_print_lines (gschem_patch_state_t
*st
)
327 static const char *op_names
[] = {
334 for (GList
*i
= st
->lines
; i
!= NULL
; i
= i
->next
) {
335 gschem_patch_line_t
*l
= i
->data
;
337 fprintf (stderr
, "NULL data on list\n");
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
,
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
);
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");
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
)
373 memset (st
, 0, sizeof *st
);
379 res
= patch_parse (st
, f
, fn
);
382 debug_print_lines (st
);
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 */
403 /******************************************************************************/
404 /*! \section build Populating the patch state hashes. */
407 /* insert item in a hash table of slists */
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
);
424 /*! \brief Make an object known to a patch state.
429 gschem_patch_state_build (gschem_patch_state_t
*st
, OBJECT
*o
)
436 refdes
= o_attrib_search_object_attribs_by_name (o
, "refdes", 0);
440 /* map the component */
441 build_insert_hash_list (st
->comps
, g_strdup (refdes
), o
);
444 for (GList
*i
= o
->complex->prim_objs
; i
!= NULL
; i
= i
->next
) {
445 OBJECT
*sub
= i
->data
;
448 pin
= o_attrib_search_object_attribs_by_name (sub
, "pinnumber", 0);
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);
455 build_insert_hash_list (st
->pins
, full_name
,
456 alloc_pin (sub
, NULL
));
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
)
472 if (strncmp (attrib
->text
->string
, "net=", 4) == 0) {
473 char *net
= attrib
->text
->string
+ 4;
474 char *pinlist
= strchr (net
, ':');
476 if (pinlist
!= NULL
) {
478 char *net_name
= NULL
;
480 net_len
= pinlist
- net
;
482 net_name
= g_strndup (net
, net_len
);
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
);
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);
496 build_insert_hash_list (st
->pins
, full_name
,
497 alloc_pin (o
, g_strdup (net_name
)));
511 /* what to do with nets? */
513 //printf ("type: '%c'\n", o->type);
516 /* ignore floating pins */
520 /* ignore all graphical objects */
535 /******************************************************************************/
536 /*! \section execute Executing the patch state. */
540 free_key (gpointer key
, gpointer value
, gpointer user_data
)
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
);
551 hit
->loc_name
= loc_name
;
552 hit
->action
= action
;
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.
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
) {
583 open
= g_list_remove (open
, o
);
585 /* ... check if it's not yet found */
586 if (g_hash_table_lookup (found
, o
) == NULL
) {
589 val
= hashval (user_ctx
, o
);
592 g_hash_table_insert (found
, o
, val
);
593 open
= s_conn_return_others (open
, o
);
600 /* return the name of the object and add relevant objects to a
601 name->obj hash in user_ctx */
603 exec_check_conn_hashval (void *user_ctx
, OBJECT
*o
)
605 gchar
*name
= NULL
, *tmp
;
606 GHashTable
*name2obj
= user_ctx
;
610 tmp
= o_attrib_search_object_attribs_by_name (o
, "netname", 0);
612 name
= g_strdup_printf ("%c%s", OBJ_NET
, tmp
);
614 g_hash_table_insert (name2obj
, name
, o
);
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 */
622 if (o
->parent
!= NULL
) {
623 gchar
*oname
, *pname
;
625 oname
= o_attrib_search_object_attribs_by_name (o
->parent
, "refdes", 0);
628 pname
= o_attrib_search_object_attribs_by_name (o
, "pinnumber", 0);
629 name
= g_strdup_printf ("%c%s-%s", OBJ_PIN
, (char *) oname
,
633 g_hash_table_insert (name2obj
, name
, o
);
640 /* Build a name->object hash of everything connected to a pin */
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
);
655 exec_free_conns (GHashTable
*connections
)
657 g_hash_table_foreach_remove (connections
, free_key
, NULL
);
658 g_hash_table_destroy (connections
);
662 exec_debug_print_conns (GHashTable
*connections
)
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
);
673 exec_conn_pretend (gschem_patch_line_t
*patch
, GList
**net
, int del
)
676 for (GList
*np
= *net
; np
!= NULL
; ) {
677 char *lname
= np
->data
;
679 if (strcmp (lname
, patch
->id
) == 0) {
680 *net
= g_list_remove (*net
, lname
);
685 *net
= g_list_prepend (*net
, g_strdup (patch
->id
));
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
;
695 int alloced
= 0, connected
;
699 printf ("exec %d:\n", del
);
701 if (pin
->net
== NULL
) {
702 connections
= exec_list_conns (pin
->obj
);
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;
711 buff
= malloc (alloced
);
714 memcpy (buff
+ 1, patch
->arg1
.net_name
, len
+ 1);
715 connected
= g_hash_table_lookup (connections
, buff
) != NULL
;
718 connected
= strcmp (patch
->arg1
.net_name
, pin
->net
) == 0;
719 buff
= g_strdup (pin
->net
);
723 /* Ugly hack: do not complain about (missing) connections to unnamed nets */
724 if (strncmp (buff
+ offs
, "unnamed_net", 11) != 0) {
727 gchar
*tmp
= g_strdup_printf (_("disconnect from net %s"), buff
+ offs
);
728 msg
= g_string_new (tmp
);
733 gchar
*tmp
= g_strdup_printf (_("connect to net %s"), buff
+ offs
);
734 msg
= g_string_new (tmp
);
740 if (connections
!= NULL
) {
741 /* check if we still have a connection to any of the pins */
742 GString
*pin_msg
= NULL
;
744 for (GList
*np
= *net
; np
!= NULL
; np
= np
->next
) {
746 len
= strlen (np
->data
);
747 if (len
+ 2 > alloced
) {
748 alloced
= len
+ 2 + 256;
750 buff
= malloc (alloced
);
753 memcpy (buff
+ 1, np
->data
, len
+ 1);
754 target
= g_hash_table_lookup (connections
, buff
);
755 if (target
== pin
->obj
)
757 if ((target
!= NULL
&& del
) || (target
== NULL
&& !del
)) {
759 pin_msg
= g_string_new (NULL
);
761 g_string_append (pin_msg
, ", ");
762 g_string_append (pin_msg
, buff
+ 1);
766 if (pin_msg
!= NULL
) {
768 msg
= g_string_new (NULL
);
770 g_string_append (msg
, "; ");
771 gchar
*tmp
= g_strdup_printf (del
? ngettext ("disconnect from pin %s",
772 "disconnect from pins %s",
774 : ngettext ("connect to pin %s",
775 "connect to pins %s",
777 g_string_free (pin_msg
, FALSE
));
778 g_string_append (msg
, tmp
);
781 exec_free_conns (connections
);
787 /* pretend that the item is resolved: update patch netlists */
788 exec_conn_pretend (patch
, net
, del
);
791 return g_slist_prepend (hits
, alloc_hit (pin
->obj
,
792 g_strdup (patch
->id
),
793 g_string_free (msg
, FALSE
)));
800 exec_check_attrib (GSList
*hits
, gschem_patch_line_t
*patch
, OBJECT
*comp
)
803 o_attrib_search_object_attribs_by_name (comp
, patch
->arg1
.attrib_name
, 0);
804 if (attr_val
== NULL
)
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
));
818 /*! \brief Retrieve a list of hits from a fully built patch state.
820 * \returns a singly-linked list of hits in reverse order
823 gschem_patch_state_execute (gschem_patch_state_t
*st
)
826 GSList
*pins
, *comps
;
830 for (GList
*i
= st
->lines
; i
!= NULL
; i
= i
->next
) {
831 gschem_patch_line_t
*l
= i
->data
;
833 fprintf (stderr
, "NULL data on list\n");
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
);
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"),
849 hits
= g_slist_prepend (hits
, alloc_hit (NULL
, not_found
, msg
));
850 exec_conn_pretend (l
, &net
, del
);
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 */
860 g_hash_table_insert (st
->nets
, l
->arg1
.net_name
, net
);
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
);
870 gchar
*not_found
= g_strdup_printf (_("%s (NOT FOUND)"), l
->id
);
871 gchar
*msg
= g_strdup_printf (_("change attribute \"%s\" to \"%s\""),
874 hits
= g_slist_prepend (hits
, alloc_hit (NULL
, not_found
, msg
));
878 case GSCHEM_PATCH_NET_INFO
:
879 /* just ignore them, we've already built data structs while parsing */
884 return g_slist_reverse (hits
);
888 /******************************************************************************/
889 /*! \section destroy Freeing the patch structures. */
893 free_patch_line (gschem_patch_line_t
*line
)
896 case GSCHEM_PATCH_DEL_CONN
:
897 case GSCHEM_PATCH_ADD_CONN
:
899 g_free (line
->arg1
.net_name
);
901 case GSCHEM_PATCH_CHANGE_ATTRIB
:
903 g_free (line
->arg1
.attrib_name
);
904 g_free (line
->arg2
.attrib_val
);
906 case GSCHEM_PATCH_NET_INFO
:
908 g_list_free_full (line
->arg1
.ids
, g_free
);
911 g_slice_free (gschem_patch_line_t
, line
);
915 free_pin (gschem_patch_pin_t
*pin
)
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.
926 gschem_patch_state_destroy (gschem_patch_state_t
*st
)
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
);
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.
963 gschem_patch_free_hit_list (GSList
*hits
)
965 g_slist_free_full (hits
, (GDestroyNotify
) free_hit
);