libgeda: Remove some exit() calls and assertions.
[geda-gaf/peter-b.git] / libgeda / src / a_basic.c
blob9be1f1b7ef7eca24ae9362fd3cfb3a86b1530f53
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 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 a_basic.c
22 * \brief basic libgeda read and write functions
24 #include <config.h>
25 #include <version.h>
27 #include <stdio.h>
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
32 #include "libgeda_priv.h"
34 #ifdef HAVE_LIBDMALLOC
35 #include <dmalloc.h>
36 #endif
38 /*! \brief Get the file header string.
39 * \par Function Description
40 * This function simply returns the DATE_VERSION and
41 * FILEFORMAT_VERSION formatted as a gEDA file header.
43 * \warning <em>Do not</em> free the returned string.
45 const gchar *o_file_format_header()
47 static gchar *header = NULL;
49 if (header == NULL)
50 header = g_strdup_printf("v %s %u\n", PACKAGE_DATE_VERSION,
51 FILEFORMAT_VERSION);
53 return header;
56 /*! \brief "Save" a file into a string buffer
57 * \par Function Description
58 * This function saves a whole schematic into a buffer in libgeda
59 * format. The buffer should be freed when no longer needed.
61 * \param [in] toplevel The current TOPLEVEL.
62 * \param [in] object_list The head of a GList of OBJECTs to save.
63 * \returns a buffer containing schematic data or NULL on failure.
65 gchar *o_save_buffer (TOPLEVEL *toplevel, const GList *object_list)
67 GString *acc;
68 gchar *buffer;
70 if (toplevel == NULL) return NULL;
72 acc = g_string_new (o_file_format_header());
74 buffer = o_save_objects (toplevel, object_list, FALSE);
75 g_string_append (acc, buffer);
76 g_free (buffer);
78 return g_string_free (acc, FALSE);
81 /*! \brief Save a series of objects into a string buffer
82 * \par Function Description
83 * This function recursively saves a set of objects into a buffer in
84 * libgeda format. User code should not normally call this function;
85 * they should call o_save_buffer() instead.
87 * With save_attribs passed as FALSE, attribute objects are skipped over,
88 * and saved separately - after the objects they are attached to. When
89 * we recurse for saving out those attributes, the function must be called
90 * with save_attribs passed as TRUE.
92 * \param [in] toplevel A TOPLEVEL structure.
93 * \param [in] object_list The head of a GList of objects to save.
94 * \param [in] save_attribs Should attribute objects encounterd be saved?
95 * \returns a buffer containing schematic data or NULL on failure.
97 gchar *o_save_objects (TOPLEVEL *toplevel, const GList *object_list, gboolean save_attribs)
99 OBJECT *o_current;
100 const GList *iter;
101 gchar *out;
102 GString *acc;
103 gboolean already_wrote = FALSE;
105 acc = g_string_new("");
107 iter = object_list;
109 while ( iter != NULL ) {
110 o_current = (OBJECT *)iter->data;
112 if (save_attribs || o_current->attached_to == NULL) {
114 switch (o_current->type) {
116 case(OBJ_LINE):
117 out = o_line_save(toplevel, o_current);
118 break;
120 case(OBJ_NET):
121 out = o_net_save(toplevel, o_current);
122 break;
124 case(OBJ_BUS):
125 out = o_bus_save(toplevel, o_current);
126 break;
128 case(OBJ_BOX):
129 out = o_box_save(toplevel, o_current);
130 break;
132 case(OBJ_CIRCLE):
133 out = o_circle_save(toplevel, o_current);
134 break;
136 case(OBJ_COMPLEX):
137 out = o_complex_save(toplevel, o_current);
138 g_string_append_printf(acc, "%s\n", out);
139 already_wrote = TRUE;
140 g_free(out); /* need to free here because of the above flag */
142 if (o_complex_is_embedded(o_current)) {
143 g_string_append(acc, "[\n");
145 out = o_save_objects(toplevel, o_current->complex->prim_objs, FALSE);
146 g_string_append (acc, out);
147 g_free(out);
149 g_string_append(acc, "]\n");
151 break;
153 case(OBJ_PLACEHOLDER): /* new type by SDB 1.20.2005 */
154 out = o_complex_save(toplevel, o_current);
155 break;
157 case(OBJ_TEXT):
158 out = o_text_save(toplevel, o_current);
159 break;
161 case(OBJ_PATH):
162 out = o_path_save(toplevel, o_current);
163 break;
165 case(OBJ_PIN):
166 out = o_pin_save(toplevel, o_current);
167 break;
169 case(OBJ_ARC):
170 out = o_arc_save(toplevel, o_current);
171 break;
173 case(OBJ_PICTURE):
174 out = o_picture_save(toplevel, o_current);
175 break;
177 default:
178 /*! \todo Maybe we can continue instead of just failing
179 * completely? In any case, failing gracefully is better
180 * than killing the program, which is what this used to
181 * do... */
182 g_critical (_("o_save_objects: object %p has unknown type '%c'\n"),
183 o_current, o_current->type);
184 /* Dump string built so far */
185 g_string_free (acc, TRUE);
186 return NULL;
189 /* output the line */
190 if (!already_wrote) {
191 g_string_append_printf(acc, "%s\n", out);
192 g_free(out);
193 } else {
194 already_wrote = FALSE;
197 /* save any attributes */
198 if (o_current->attribs != NULL) {
199 g_string_append (acc, "{\n");
201 out = o_save_objects (toplevel, o_current->attribs, TRUE);
202 g_string_append (acc, out);
203 g_free(out);
205 g_string_append (acc, "}\n");
209 iter = g_list_next (iter);
212 return g_string_free (acc, FALSE);
215 /*! \brief Save a file
216 * \par Function Description
217 * This function saves the data in a libgeda format to a file
219 * \bug g_access introduces a race condition in certain cases, but
220 * solves bug #698565 in the normal use-case
222 * \param [in] toplevel The current TOPLEVEL.
223 * \param [in] object_list The head of a GList of OBJECTs to save.
224 * \param [in] filename The filename to save the data to.
225 * \param [in,out] err #GError structure for error reporting.
226 * \return 1 on success, 0 on failure.
228 int o_save (TOPLEVEL *toplevel, const GList *object_list,
229 const char *filename, GError **err)
231 char *buffer;
233 /* Check to see if real filename is writable; if file doesn't exists
234 we assume all is well */
235 if (g_file_test(filename, G_FILE_TEST_EXISTS) &&
236 g_access(filename, W_OK) != 0) {
237 g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_PERM,
238 _("File %s is read-only"), filename);
239 return 0;
242 buffer = o_save_buffer (toplevel, object_list);
243 if (!g_file_set_contents (filename, buffer, strlen(buffer), err)) {
244 g_free (buffer);
245 return 0;
247 g_free (buffer);
249 return 1;
252 /*! \brief Read a memory buffer
253 * \par Function Description
254 * This function reads data in libgeda format from a memory buffer.
256 * If the size argument is negative, the buffer is assumed to be
257 * null-terminated.
259 * The name argument is used for debugging, and should be set to a
260 * meaningful string (e.g. the name of the file the data is from).
262 * \param [in,out] toplevel The current TOPLEVEL structure.
263 * \param [in] object_list The object_list to read data to.
264 * \param [in] buffer The memory buffer to read from.
265 * \param [in] size The size of the buffer.
266 * \param [in] name The name to describe the data with.
267 * \return GList of objects if successful read, or NULL on error.
269 GList *o_read_buffer (TOPLEVEL *toplevel, GList *object_list,
270 char *buffer, const int size,
271 const char *name)
273 char *line = NULL;
274 TextBuffer *tb = NULL;
276 char objtype;
277 GList *object_list_save=NULL;
278 OBJECT *new_obj=NULL;
279 GList *new_obj_list;
280 GList *iter;
281 unsigned int release_ver;
282 unsigned int fileformat_ver;
283 unsigned int current_fileformat_ver;
284 int found_pin = 0;
285 OBJECT* last_complex = NULL;
286 int itemsread = 0;
288 int embedded_level = 0;
291 /* fill version with default file format version (the current one) */
292 current_fileformat_ver = FILEFORMAT_VERSION;
294 g_return_val_if_fail ((buffer != NULL), NULL);
296 tb = s_textbuffer_new (buffer, size);
298 object_list = g_list_reverse (object_list);
300 while (1) {
302 line = s_textbuffer_next_line(tb);
303 if (line == NULL) break;
305 sscanf(line, "%c", &objtype);
307 /* Do we need to check the symbol version? Yes, but only if */
308 /* 1) the last object read was a complex and */
309 /* 2) the next object isn't the start of attributes. */
310 /* If the next object is the start of attributes, then check the */
311 /* symbol version after the attributes have been read in, see the */
312 /* STARTATTACH_ATTR case */
313 if (last_complex && objtype != STARTATTACH_ATTR)
315 /* yes */
316 /* verify symbol version (not file format but rather contents) */
317 o_complex_check_symversion(toplevel, last_complex);
318 last_complex = NULL; /* no longer need to check */
321 switch (objtype) {
323 case(OBJ_LINE):
324 new_obj = o_line_read (toplevel, line, release_ver, fileformat_ver);
325 object_list = g_list_prepend (object_list, new_obj);
326 break;
329 case(OBJ_NET):
330 new_obj = o_net_read (toplevel, line, release_ver, fileformat_ver);
331 object_list = g_list_prepend (object_list, new_obj);
332 break;
334 case(OBJ_BUS):
335 new_obj = o_bus_read (toplevel, line, release_ver, fileformat_ver);
336 object_list = g_list_prepend (object_list, new_obj);
337 break;
339 case(OBJ_BOX):
340 new_obj = o_box_read (toplevel, line, release_ver, fileformat_ver);
341 object_list = g_list_prepend (object_list, new_obj);
342 break;
344 case(OBJ_PICTURE):
345 line = g_strdup (line);
346 new_obj = o_picture_read (toplevel, line, tb, release_ver, fileformat_ver);
347 g_free (line);
348 object_list = g_list_prepend (object_list, new_obj);
349 break;
351 case(OBJ_CIRCLE):
352 new_obj = o_circle_read (toplevel, line, release_ver, fileformat_ver);
353 object_list = g_list_prepend (object_list, new_obj);
354 break;
356 case(OBJ_COMPLEX):
357 case(OBJ_PLACEHOLDER):
358 new_obj = o_complex_read (toplevel, line, release_ver, fileformat_ver);
359 object_list = g_list_prepend (object_list, new_obj);
361 /* last_complex is used for verifying symversion attribute */
362 last_complex = new_obj;
363 break;
365 case(OBJ_TEXT):
366 line = g_strdup (line);
367 new_obj = o_text_read (toplevel, line, tb, release_ver, fileformat_ver);
368 g_free (line);
369 object_list = g_list_prepend (object_list, new_obj);
370 break;
372 case(OBJ_PATH):
373 line = g_strdup(line);
374 new_obj = o_path_read (toplevel, line, tb, release_ver, fileformat_ver);
375 g_free (line);
376 object_list = g_list_prepend (object_list, new_obj);
377 break;
379 case(OBJ_PIN):
380 new_obj = o_pin_read (toplevel, line, release_ver, fileformat_ver);
381 object_list = g_list_prepend (object_list, new_obj);
382 found_pin++;
383 break;
385 case(OBJ_ARC):
386 new_obj = o_arc_read (toplevel, line, release_ver, fileformat_ver);
387 object_list = g_list_prepend (object_list, new_obj);
388 break;
390 case(STARTATTACH_ATTR):
391 /* first is the fp */
392 /* 2nd is the object to get the attributes */
393 new_obj_list = o_read_attribs (toplevel, NULL, new_obj, tb, release_ver, fileformat_ver);
394 new_obj_list = g_list_reverse (new_obj_list);
395 object_list = g_list_concat (new_obj_list, object_list);
397 /* by now we have finished reading all the attributes */
398 /* did we just finish attaching to a complex object? */
399 if (last_complex)
401 /* yes */
402 /* verify symbol version (not file format but rather contents) */
403 o_complex_check_symversion(toplevel, last_complex);
404 last_complex = NULL;
407 /* slots only apply to complex objects */
408 if (new_obj != NULL &&
409 (new_obj->type == OBJ_COMPLEX ||
410 new_obj->type == OBJ_PLACEHOLDER)) {
411 s_slot_update_object (toplevel, new_obj);
414 new_obj = NULL;
415 break;
417 case(START_EMBEDDED):
418 new_obj = object_list->data;
420 if (new_obj != NULL &&
421 (new_obj->type == OBJ_COMPLEX ||
422 new_obj->type == OBJ_PLACEHOLDER)) {
424 object_list_save = object_list;
425 object_list = new_obj->complex->prim_objs;
427 embedded_level++;
428 } else {
429 fprintf(stderr, _("Read unexpected embedded "
430 "symbol start marker in [%s] :\n>>\n%s<<\n"),
431 name, line);
433 break;
435 case(END_EMBEDDED):
436 if (embedded_level>0) {
437 /* don't do this since objects are already
438 * stored/read translated
439 * o_complex_translate_world (toplevel, object_list->x,
440 * object_list->y, object_list->complex);
442 object_list = g_list_reverse (object_list);
444 new_obj = object_list_save->data;
445 new_obj->complex->prim_objs = object_list;
446 object_list = object_list_save;
448 /* set the parent field now */
449 for (iter = new_obj->complex->prim_objs;
450 iter != NULL; iter = g_list_next (iter)) {
451 OBJECT *tmp = iter->data;
452 tmp->parent = new_obj;
455 o_recalc_single_object (toplevel, new_obj);
457 embedded_level--;
458 } else {
459 fprintf(stderr, _("Read unexpected embedded "
460 "symbol end marker in [%s] :\n>>\n%s<<\n"),
461 name, line);
463 break;
465 case(ENDATTACH_ATTR):
466 /* this case is never hit, since the } is consumed by o_read_attribs */
467 break;
469 case(INFO_FONT):
470 /* NOP */
471 break;
473 case(COMMENT):
474 /* do nothing */
475 break;
477 case(VERSION_CHAR):
478 itemsread = sscanf(line, "v %u %u\n", &release_ver, &fileformat_ver);
480 /* 20030921 was the last version which did not have a fileformat */
481 /* version. The below latter test should not happen, but it is here */
482 /* just in in case. */
483 if (release_ver <= VERSION_20030921 || itemsread == 1) {
484 fileformat_ver = 0;
487 if (fileformat_ver == 0) {
488 s_log_message(_("Read an old format sym/sch file!\n"
489 "Please run g[sym|sch]update on:\n[%s]\n"), name);
491 break;
493 default:
494 fprintf(stderr, _("Read garbage in [%s] :\n>>\n%s<<\n"),
495 name, line);
496 new_obj = NULL;
497 break;
502 /* Was the very last thing we read a complex and has it not been checked */
503 /* yet? This would happen if the complex is at the very end of the file */
504 /* and had no attached attributes */
505 if (last_complex)
507 o_complex_check_symversion(toplevel, last_complex);
508 last_complex = NULL; /* no longer need to check */
511 if (found_pin) {
512 if (release_ver <= VERSION_20020825) {
513 o_pin_update_whichend (toplevel, object_list, found_pin);
517 tb = s_textbuffer_free(tb);
519 object_list = g_list_reverse (object_list);
521 return(object_list);
525 /*! \brief Read a file
526 * \par Function Description
527 * This function reads a file in libgeda format.
529 * \param [in,out] toplevel The current TOPLEVEL structure.
530 * \param [in] object_list The object_list to read data to.
531 * \param [in] filename The filename to read from.
532 * \param [in,out] err #GError structure for error reporting, or
533 * NULL to disable error reporting
534 * \return object_list if successful read, or NULL on error.
536 GList *o_read (TOPLEVEL *toplevel, GList *object_list, char *filename,
537 GError **err)
539 char *buffer = NULL;
540 size_t size;
541 GList *result;
543 /* Return NULL if error reporting is enabled and the return location
544 * for an error isn't NULL. */
545 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
547 if (!g_file_get_contents(filename, &buffer, &size, err)) {
548 return NULL;
551 /* Parse file contents */
552 result = o_read_buffer (toplevel, object_list, buffer, size, filename);
553 g_free (buffer);
554 return result;
557 /*! \brief Scale a set of lines.
558 * \par Function Description
559 * This function takes a list of lines and scales them
560 * by the values of x_scale and y_scale.
562 * \param [in] toplevel The current TOPLEVEL object.
563 * \param [in,out] list The list with lines to scale.
564 * \param [in] x_scale The x scale value for the lines.
565 * \param [in] y_scale The y scale value for the lines.
567 * \todo this really doesn't belong here. you need more of a core routine
568 * first. yes.. this is the core routine, just strip out the drawing
569 * stuff
570 * move it to o_complex_scale
572 void o_scale (TOPLEVEL *toplevel, GList *list, int x_scale, int y_scale)
574 OBJECT *o_current;
575 GList *iter;
577 /* this is okay if you just hit scale and have nothing selected */
578 if (list == NULL) {
579 return;
582 iter = list;
583 while (iter != NULL) {
584 o_current = (OBJECT *)iter->data;
585 switch(o_current->type) {
586 case(OBJ_LINE):
587 o_line_scale_world(toplevel, x_scale, y_scale, o_current);
588 break;
590 iter = g_list_next (iter);