Bump gEDA version
[geda-gaf.git] / libgeda / src / o_attrib.c
blobb94dc0736353d14be54d94722f35652cd9f5ff41
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /*! \file o_attrib.c
22 * \brief utility functions for attributes
24 * Attributes are normal text objects. An attribute is a text object
25 * that has a text string that is delimited by an equal "=" character.
26 * The part before the equal character is called <b>name</b> the
27 * part of the string behind the equal character is called <b>value</b>
29 * Attributes are attached to OBJECTs (st_object). Each attribute has
30 * a reference to the object it is attached to. Each object that has
31 * attributes has a list of pionters to its attributes.
33 * \image html o_attrib_overview.png
34 * \image latex o_attrib_overview.pdf "attribute overview" width=14cm
36 * \note
37 * Be sure in o_copy o_move o_delete you maintain the attributes
38 * delete is a bare, because you will have to unattach the other end
39 * and in o_save o_read as well
40 * and in o_select when selecting objects, select the attributes
43 #include <config.h>
45 #include <stdio.h>
46 #ifdef HAVE_STRING_H
47 #include <string.h>
48 #endif
49 #include <math.h>
51 #include "libgeda_priv.h"
54 /*! \brief Add an attribute to an existing attribute list.
55 * \par Function Description
56 * Add an attribute to an existing attribute list.
58 * \param [in] toplevel The TOPLEVEL object.
59 * \param [in] object The OBJECT we're adding the attribute to.
60 * \param [in] item The item you want to add as an attribute.
61 * \return nothing.
63 void o_attrib_add(TOPLEVEL *toplevel, OBJECT *object, OBJECT *item)
65 /* Add link from item to attrib listing */
66 item->attached_to = object;
67 object->attribs = g_list_append (object->attribs, item);
71 /*! \brief Check whether a attrib is attached to another object
72 * \par Function Description
73 * This function checks whether the object \a attrib is attached to
74 * the \a object.
76 * \param [in] toplevel The TOPLEVEL object.
77 * \param [in] attrib The attribute to be checket.
78 * \param [in] object The object where you want to add item as an attribute.
79 * \return TRUE if attrib is an attribute of object, FALSE otherwise
81 gboolean o_attrib_is_attached (TOPLEVEL *toplevel,
82 OBJECT *attrib, OBJECT *object)
84 if (attrib == NULL || object == NULL)
85 return FALSE;
87 if (attrib->attached_to == object)
88 return TRUE;
90 return FALSE;
94 /*! \brief Attach existing attribute to an object.
95 * \par Function Description
96 * Attach existing attribute to an object.
98 * \param [in] toplevel The TOPLEVEL object.
99 * \param [in] attrib The attribute to be added.
100 * \param [out] object The object where you want to add item as an attribute.
101 * \param [in] set_color Whether or not we should set the new attribute's color.
103 void o_attrib_attach (TOPLEVEL *toplevel, OBJECT *attrib, OBJECT *object,
104 int set_color)
106 g_return_if_fail (attrib != NULL);
107 g_return_if_fail (object != NULL);
109 /* is the object already part of the list ? */
110 if (g_list_find (object->attribs, attrib)) {
111 g_warning (_("Attribute [%s] already attached\n"), attrib->text->string);
112 return;
115 if (attrib->type != OBJ_TEXT) {
116 g_warning (_("Attempt to attach non text item as an attribute!\n"));
117 return;
120 if (attrib->attached_to != NULL) {
121 g_warning (_("Attempt to attach attribute [%s] to more than one object\n"),
122 attrib->text->string);
123 return;
126 o_attrib_add (toplevel, object, attrib);
128 if (set_color)
129 o_set_color (toplevel, attrib, ATTRIBUTE_COLOR);
133 /*! \brief Attach list of existing attributes to an object.
134 * \par Function Description
135 * Attach list of existing attributes to an object.
137 * \param [in] toplevel The TOPLEVEL object.
138 * \param [in] attr_list The list of attributes to be added.
139 * \param [out] object The object where you want to add item as an attribute.
140 * \param [in] set_color Whether or not we should set the new attribute's color.
142 void o_attrib_attach_list (TOPLEVEL *toplevel,
143 GList *attr_list, OBJECT *object, int set_color)
145 GList *iter;
147 for (iter = attr_list; iter != NULL; iter = g_list_next (iter))
148 o_attrib_attach (toplevel, iter->data, object, set_color);
152 /*! \brief Detach all attribute items in a list.
153 * \par Function Description
154 * Detach all attributes from an object.
156 * \param [in] toplevel The TOPLEVEL object.
157 * \param [in,out] object The object whos attributes to detach.
159 void o_attrib_detach_all(TOPLEVEL *toplevel, OBJECT *object)
161 OBJECT *a_current;
162 GList *a_iter;
164 for (a_iter = object->attribs; a_iter != NULL;
165 a_iter = g_list_next (a_iter)) {
166 a_current = a_iter->data;
168 a_current->attached_to = NULL;
169 o_set_color (toplevel, a_current, DETACHED_ATTRIBUTE_COLOR);
172 g_list_free (object->attribs);
173 object->attribs = NULL;
176 /*! \brief Print all attributes to a Postscript document.
177 * \par Function Description
178 * Print all attributes to a Postscript document.
180 * \param [in] attributes List of attributes to print.
182 void o_attrib_print(GList *attributes)
184 OBJECT *a_current;
185 GList *a_iter;
187 a_iter = attributes;
189 while (a_iter != NULL) {
190 a_current = a_iter->data;
191 printf("Attribute points to: %s\n", a_current->name);
192 if (a_current->text) {
193 printf("\tText is: %s\n", a_current->text->string);
196 a_iter = g_list_next (a_iter);
200 /*! \todo Finish function.
201 * \brief Remove an attribute item from an attribute list.
202 * \par Function Description
203 * This function removes the given attribute from an attribute list.
204 * This function should be used when detaching an attribute.
206 * \param [in] toplevel The TOPLEVEL object.
207 * \param [in] list The attribute list to remove attribute from.
208 * \param [in] remove The OBJECT to remove from list.
210 void o_attrib_remove(TOPLEVEL *toplevel, GList **list, OBJECT *remove)
212 g_return_if_fail (remove != NULL);
214 remove->attached_to = NULL;
216 *list = g_list_remove (*list, remove);
219 /*! \brief Read attributes from a buffer.
220 * \par Function Description
221 * Read attributes from a TextBuffer.
223 * \param [in] toplevel The TOPLEVEL object.
224 * \param [in] object_to_get_attribs Object which gets these attribs.
225 * \param [in] tb The text buffer to read from.
226 * \param [in] release_ver libgeda release version number.
227 * \param [in] fileformat_ver file format version number.
228 * \return GList of attributes read, or NULL on error.
230 GList *o_read_attribs (TOPLEVEL *toplevel,
231 OBJECT *object_to_get_attribs,
232 TextBuffer *tb,
233 unsigned int release_ver, unsigned int fileformat_ver, GError ** err)
235 GList *object_list = NULL;
236 OBJECT *new_obj;
237 const char *line = NULL;
238 char objtype;
239 int ATTACH=FALSE;
241 while (1) {
243 line = s_textbuffer_next_line (tb);
244 if (line == NULL) break;
246 sscanf(line, "%c", &objtype);
247 switch (objtype) {
249 case(OBJ_LINE):
250 if ((new_obj = o_line_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
251 goto error;
252 object_list = g_list_prepend (object_list, new_obj);
253 break;
256 case(OBJ_NET):
257 if ((new_obj = o_net_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
258 goto error;
259 object_list = g_list_prepend (object_list, new_obj);
260 break;
262 case(OBJ_BUS):
263 if ((new_obj = o_bus_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
264 goto error;
265 object_list = g_list_prepend (object_list, new_obj);
266 break;
268 case(OBJ_BOX):
269 if ((new_obj = o_box_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
270 goto error;
271 object_list = g_list_prepend (object_list, new_obj);
272 break;
274 case(OBJ_CIRCLE):
275 if ((new_obj = o_circle_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
276 goto error;
277 object_list = g_list_prepend (object_list, new_obj);
278 break;
280 case(OBJ_COMPLEX):
281 case(OBJ_PLACEHOLDER):
282 if ((new_obj = o_complex_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
283 goto error;
284 object_list = g_list_prepend (object_list, new_obj);
285 break;
287 case(OBJ_PATH):
288 new_obj = o_path_read (toplevel, line, tb, release_ver, fileformat_ver, err);
289 if (new_obj == NULL)
290 goto error;
291 object_list = g_list_prepend (object_list, new_obj);
292 break;
294 case(OBJ_PIN):
295 if ((new_obj = o_pin_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
296 goto error;
297 object_list = g_list_prepend (object_list, new_obj);
298 break;
300 case(OBJ_ARC):
301 if ((new_obj = o_arc_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
302 goto error;
303 object_list = g_list_prepend (object_list, new_obj);
304 break;
306 case(OBJ_TEXT):
307 new_obj = o_text_read (toplevel, line, tb, release_ver, fileformat_ver, err);
308 if (new_obj == NULL)
309 goto error;
310 object_list = g_list_prepend (object_list, new_obj);
311 ATTACH=TRUE;
313 break;
315 case(ENDATTACH_ATTR):
316 return object_list;
317 break;
320 if (ATTACH) {
321 o_attrib_attach (toplevel, new_obj, object_to_get_attribs, FALSE);
322 ATTACH=FALSE;
323 } else {
324 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Tried to attach a non-text item as an attribute"));
325 goto error;
329 /* The attribute list wasn't terminated, so it's a parse error! */
330 g_set_error (err, EDA_ERROR, EDA_ERROR_PARSE,
331 _("Unexpected end-of-file in attribute list"));
333 error:
334 s_delete_object_glist(toplevel, object_list);
335 return NULL;
339 /*! \brief Get name and value from an attribute 'name=value' string.
340 * \par Function Description
341 * This function parses the character string \a string expected to be
342 * an attribute string of the form 'name=value'.
344 * It returns TRUE if it has been able to parse the string into the
345 * name and value parts of an attribute. Otherwise it returns FALSE,
346 * in that case \a *name_ptr and \a *value_ptr are set to NULL.
348 * \a name_ptr and/or \a value_ptr can be NULL.
349 * If not NULL, the caller must g_free these returned strings.
351 * \note
352 * If you get an invalid attribute (improper) with a name and no
353 * value, then it is NOT an attribute. Also, there cannot be any
354 * spaces beside the equals sign
356 * \param [in] string String to split into name/value pair.
357 * \param [out] name_ptr The return location for the name, or NULL.
358 * \param [out] value_ptr The return location for the value, or NULL.
359 * \return TRUE on success, FALSE otherwise.
361 gboolean
362 o_attrib_string_get_name_value (const gchar *string, gchar **name_ptr, gchar **value_ptr)
364 gchar *ptr, *prev_char, *next_char;
366 if (name_ptr != NULL)
367 *name_ptr = NULL;
368 if (value_ptr != NULL)
369 *value_ptr = NULL;
371 g_return_val_if_fail (string != NULL, FALSE);
373 ptr = g_utf8_strchr (string, -1, g_utf8_get_char ("="));
374 if (ptr == NULL) {
375 return FALSE;
378 prev_char = g_utf8_find_prev_char (string, ptr);
379 next_char = g_utf8_find_next_char (ptr, NULL);
380 if (prev_char == NULL || *prev_char == ' ' ||
381 next_char == NULL || *next_char == ' ' || *next_char == '\0' ) {
382 return FALSE;
385 if (name_ptr != NULL) {
386 *name_ptr = g_strndup (string, (ptr - string));
389 if (value_ptr != NULL) {
390 *value_ptr = g_strdup (next_char);
393 return TRUE;
397 /*! \brief Get name and value from an attribute OBJECT
398 * \par Function Description
399 * See o_attrib_string_get_name_value() for more details
401 * \param [in] attrib The attribute OBJECT whos name/value to return.
402 * \param [out] name_ptr The return location for the name, or NULL.
403 * \param [out] value_ptr The return location for the value, or NULL.
404 * \return TRUE on success, FALSE otherwise.
406 gboolean
407 o_attrib_get_name_value (OBJECT *attrib, gchar **name_ptr, gchar **value_ptr)
409 g_return_val_if_fail (attrib->type == OBJ_TEXT, FALSE);
411 return o_attrib_string_get_name_value (attrib->text->string,
412 name_ptr, value_ptr);
416 /*! \brief Find all floating attributes in the given object list.
417 * \par Function Description
418 * Find all floating attributes in the given object list.
420 * \param [in] list GList of OBJECTs to search for floating attributes.
421 * \return GList of floating attributes from the input list
423 * \warning
424 * Caller must g_list_free returned list.
426 GList *o_attrib_find_floating_attribs (const GList *list)
428 GList *floating_attributes = NULL;
429 const GList *iter;
430 OBJECT *o_current;
432 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
433 o_current = iter->data;
435 /* Skip non text objects, attached attributes and text which doesn't
436 * constitute a valid attributes (e.g. general text placed on the page)
438 if (o_current->type == OBJ_TEXT &&
439 o_current->attached_to == NULL &&
440 o_attrib_get_name_value (o_current, NULL, NULL)) {
442 floating_attributes = g_list_prepend (floating_attributes, o_current);
446 return g_list_reverse (floating_attributes);
450 /*! \brief Find an attribute in a list.
451 * \par Function Description
452 * Search for attribute by name.
454 * Counter is the n'th occurance of the attribute, and starts searching
455 * from zero. Zero is the first occurance of an attribute.
457 * \param [in] list GList of attributes to search.
458 * \param [in] name Character string with attribute name to search for.
459 * \param [in] count Which occurance to return.
460 * \return The n'th attribute object in the given list with the given name.
462 OBJECT *o_attrib_find_attrib_by_name (const GList *list, char *name, int count)
464 OBJECT *a_current;
465 const GList *iter;
466 char *found_name;
467 int internal_counter = 0;
469 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
470 a_current = iter->data;
472 g_return_val_if_fail (a_current->type == OBJ_TEXT, NULL);
474 if (!o_attrib_get_name_value (a_current, &found_name, NULL))
475 continue;
477 if (strcmp (name, found_name) == 0) {
478 if (internal_counter == count) {
479 g_free (found_name);
480 return a_current;
482 internal_counter++;
485 g_free (found_name);
488 return NULL;
492 /*! \brief Search attribute list by name.
493 * \par Function Description
494 * Search for attribute by name.
496 * Counter is the n'th occurance of the attribute, and starts searching
497 * from zero. Zero is the first occurance of an attribute.
499 * \param [in] list GList of attributes to search.
500 * \param [in] name Character string with attribute name to search for.
501 * \param [in] counter Which occurance to return.
502 * \return Character string with attribute value, NULL otherwise.
504 static char *o_attrib_search_attrib_list_by_name (const GList *list, char *name, int counter)
506 OBJECT *attrib;
507 char *value = NULL;
509 attrib = o_attrib_find_attrib_by_name (list, name, counter);
511 if (attrib != NULL)
512 o_attrib_get_name_value (attrib, NULL, &value);
514 return value;
518 /*! \brief Search floating attribute by name.
519 * \par Function Description
520 * Search for attribute by name.
522 * Counter is the n'th occurance of the attribute, and starts searching
523 * from zero. Zero is the first occurance of an attribute.
525 * \param [in] list GList of OBJECTs to search for floating attributes.
526 * \param [in] name Character string with attribute name to search for.
527 * \param [in] counter Which occurance to return.
528 * \return Character string with attribute value, NULL otherwise.
530 * \warning
531 * Caller must g_free returned character string.
533 char *o_attrib_search_floating_attribs_by_name (const GList *list, char *name, int counter)
535 char *result;
536 GList *attributes;
538 attributes = o_attrib_find_floating_attribs (list);
539 result = o_attrib_search_attrib_list_by_name (attributes, name, counter);
540 g_list_free (attributes);
542 return result;
546 /*! \brief Search attached attributes by name.
547 * \par Function Description
548 * Search for attribute by name.
550 * Counter is the n'th occurance of the attribute, and starts searching
551 * from zero. Zero is the first occurance of an attribute.
553 * \param [in] object The OBJECT whos attached attributes to search.
554 * \param [in] name Character string with attribute name to search for.
555 * \param [in] counter Which occurance to return.
556 * \return Character string with attribute value, NULL otherwise.
558 * \warning
559 * Caller must g_free returned character string.
561 char *o_attrib_search_attached_attribs_by_name (OBJECT *object, char *name, int counter)
563 return o_attrib_search_attrib_list_by_name (object->attribs, name, counter);
567 /*! \brief Search inherited attribute by name.
568 * \par Function Description
569 * Search for attribute by name.
571 * Counter is the n'th occurance of the attribute, and starts searching
572 * from zero. Zero is the first occurance of an attribute.
574 * \param [in] object The OBJECT whos inherited attributes to search.
575 * \param [in] name Character string with attribute name to search for.
576 * \param [in] counter Which occurance to return.
577 * \return Character string with attribute value, NULL otherwise.
579 * \warning
580 * Caller must g_free returned character string.
582 char *o_attrib_search_inherited_attribs_by_name (OBJECT *object, char *name, int counter)
584 g_return_val_if_fail (object->type == OBJ_COMPLEX ||
585 object->type == OBJ_PLACEHOLDER, NULL);
587 return o_attrib_search_floating_attribs_by_name (object->complex->prim_objs, name, counter);
591 /*! \brief Search attributes of object by name.
592 * \par Function Description
593 * Search for attribute by name.
595 * Counter is the n'th occurance of the attribute, and starts searching
596 * from zero. Zero is the first occurance of an attribute.
598 * \param [in] object OBJECT who's attributes to search.
599 * \param [in] name Character string with attribute name to search for.
600 * \param [in] counter Which occurance to return.
601 * \return Character string with attribute value, NULL otherwise.
603 * \warning
604 * Caller must g_free returned character string.
606 char *o_attrib_search_object_attribs_by_name (OBJECT *object, char *name, int counter)
608 char *result;
609 GList *attributes;
611 attributes = o_attrib_return_attribs (object);
612 result = o_attrib_search_attrib_list_by_name (attributes, name, counter);
613 g_list_free (attributes);
615 return result;
619 /*! \brief Get all attached attributes of the specified OBJECT.
620 * \par Function Description
621 * This function returns all attributes of the specified object.
623 * The returned GList should be freed using the #g_list_free().
625 * This function aggregates the attached and inherited attributes
626 * belonging to a given OBJECT. (inherited attributes are those
627 * which live as toplevel un-attached attributes inside in a
628 * complex OBJECT's prim_objs).
630 * \param [in] object OBJECT whos attributes to return.
631 * \return A GList of attributes belinging to the passed object.
633 GList * o_attrib_return_attribs (OBJECT *object)
635 GList *attribs = NULL;
636 GList *inherited_attribs;
637 OBJECT *a_current;
638 GList *a_iter;
640 g_return_val_if_fail (object != NULL, NULL);
642 /* Directly attached attributes */
643 for (a_iter = object->attribs; a_iter != NULL;
644 a_iter = g_list_next (a_iter)) {
645 a_current = a_iter->data;
647 if (a_current->type != OBJ_TEXT)
648 continue;
650 /* Don't add invalid attributes to the list */
651 if (!o_attrib_get_name_value (a_current, NULL, NULL))
652 continue;
654 attribs = g_list_prepend (attribs, a_current);
657 attribs = g_list_reverse (attribs);
659 /* Inherited attributes (inside complex objects) */
660 if (object->type == OBJ_COMPLEX ||
661 object->type == OBJ_PLACEHOLDER) {
663 inherited_attribs =
664 o_attrib_find_floating_attribs (object->complex->prim_objs);
666 attribs = g_list_concat (attribs, inherited_attribs);
669 return attribs;
673 /*! \brief Query whether a given attribute OBJECT is "inherited"
674 * \par Function Description
675 * This function returns TRUE if the given attribute OBJECT is a
676 * toplevel un-attached attribute inside a complex's prim_objs.
678 * \param [in] attrib OBJECT who's status to query.
679 * \return TRUE if the given attribute is inside a symbol
681 int o_attrib_is_inherited (OBJECT *attrib)
683 return (attrib->attached_to == NULL &&
684 attrib->parent != NULL);