1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998, 1999 Alexander Larsson
3 *
4 * Custom Objects -- objects defined in XML rather than C.
5 * Copyright (C) 1999 James Henstridge.
6 *
7 * Custom shape loading on demand.
8 * Copyright (C) 2007 Hans Breuer.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24 #include <config.h>
25
26 #include "shape_info.h"
27 #include "custom_util.h"
28 #include <string.h>
29 #include <stdarg.h>
30 #include <glib.h>
31 #include <glib/gstdio.h>
32 #include <libxml/parser.h>
33
34 /*
35 * Instead of parsing the complete shape file at start-up we read only
36 * the minimal info required to register the type. This should speed
37 * up startup a lot and also spare some memory for shapes never used
38 * at runtime.
39 *
40 * There are so many shapes in dia that on my computer there is a difference
41 * of 16MB vs. 36MB total memory consumption with none vs. all of them loaded.
42 *
43 * The startup time ...
44 */
45
46 typedef enum {
47 READ_ON = 0,
48 READ_NAME = 1,
49 READ_ICON = 2,
50 READ_DONE = 3
51 } eState;
52
53 typedef struct _Context Context;
54 struct _Context {
55 ShapeInfo* si;
56 eState state;
57 };
58
59 static void
startElementNs(void * ctx,const xmlChar * localname,const xmlChar * prefix,const xmlChar * URI,int nb_namespaces,const xmlChar ** namespaces,int nb_attributes,int nb_defaulted,const xmlChar ** attributes)60 startElementNs (void *ctx,
61 const xmlChar *localname,
62 const xmlChar *prefix,
63 const xmlChar *URI,
64 int nb_namespaces,
65 const xmlChar **namespaces,
66 int nb_attributes,
67 int nb_defaulted,
68 const xmlChar **attributes)
69 {
70 Context* context = (Context*)ctx;
71
72 if (READ_DONE == context->state)
73 /* no more to do */;
74 else if (strncmp ((const char*)localname, "name", 4) == 0)
75 context->state = READ_NAME;
76 else if (strncmp ((const char*)localname, "icon", 4) == 0)
77 context->state = READ_ICON;
78 else if (context->si->name != NULL && context->si->icon != NULL)
79 context->state = READ_DONE;
80 else
81 context->state = READ_ON;
82 }
83
84 static void
_characters(void * ctx,const xmlChar * ch,int len)85 _characters (void *ctx,
86 const xmlChar *ch,
87 int len)
88 {
89 Context* context = (Context*)ctx;
90
91 if (READ_DONE == context->state)
92 /* no more to do */;
93 if (READ_NAME == context->state) {
94 gchar *prev = context->si->name;
95 if (!prev)
96 context->si->name = g_strndup ((const gchar*)ch, len);
97 else {
98 gchar *now = g_strndup ((const gchar*)ch, len);
99 context->si->name = g_strconcat (prev, now, NULL);
100 g_free (prev);
101 g_free (now);
102 }
103 } else if (READ_ICON == context->state) {
104 gchar *prev = context->si->icon;
105 if (!prev)
106 context->si->icon = g_strndup ((const char*)ch, len);
107 else {
108 gchar *now = g_strndup ((const char*)ch, len);
109 context->si->icon = g_strconcat (prev, now, NULL);
110 g_free (prev);
111 g_free (now);
112 }
113 }
114 }
115
116 static void
endElementNs(void * ctx,const xmlChar * localname,const xmlChar * prefix,const xmlChar * URI)117 endElementNs (void *ctx,
118 const xmlChar *localname,
119 const xmlChar *prefix,
120 const xmlChar *URI)
121 {
122 Context* context = (Context*)ctx;
123
124 if (READ_DONE == context->state)
125 return;
126
127 if (READ_NAME == context->state)
128 if (!context->si->name || context->si->name[0] == '\0')
129 g_warning ("Shape (%s) missing type name", context->si->filename);
130 if (READ_ICON == context->state)
131 if (!context->si->icon || context->si->icon[0] == '\0')
132 g_warning ("Shape (%s) missing icon name", context->si->filename);
133 if ( (READ_NAME == context->state || READ_ICON == context->state)
134 && context->si->name && context->si->icon)
135 context->state = READ_DONE;
136 else
137 context->state = READ_ON;
138 }
139
140 static void
_error(void * ctx,const char * msg,...)141 _error (void *ctx,
142 const char * msg,
143 ...)
144 {
145 Context* context = (Context*)ctx;
146 va_list args;
147
148 if (READ_DONE == context->state)
149 return; /* we are ready, not interested in further complains */
150 va_start(args, msg);
151 g_print ("Error: %s\n", context->si->filename);
152 g_vprintf (msg, args);
153 g_print ("\n");
154 va_end(args);
155 }
156
157 static void
_warning(void * ctx,const char * msg,...)158 _warning (void *ctx,
159 const char * msg,
160 ...)
161 {
162 Context* context = (Context*)ctx;
163 va_list args;
164
165 if (READ_DONE == context->state)
166 return; /* we are ready, not interested in further complains */
167 va_start(args, msg);
168 g_print ("Warning: %s\n", context->si->filename);
169 g_vprintf (msg, args);
170 g_print ("\n");
171 va_end(args);
172 }
173
174 gboolean
shape_typeinfo_load(ShapeInfo * info)175 shape_typeinfo_load (ShapeInfo* info)
176 {
177 static xmlSAXHandler saxHandler;
178 static gboolean once = FALSE;
179 #define BLOCKSIZE 512
180 char buffer[BLOCKSIZE];
181 FILE *f;
182 int n;
183 Context ctx = { info, READ_ON };
184
185 g_assert (info->filename != NULL);
186
187 if (!once) {
188 LIBXML_TEST_VERSION
189
190 memset(&saxHandler, 0, sizeof(saxHandler));
191 saxHandler.initialized = XML_SAX2_MAGIC;
192 saxHandler.startElementNs = startElementNs;
193 saxHandler.characters = _characters;
194 saxHandler.endElementNs = endElementNs;
195 saxHandler.error = _error;
196 saxHandler.warning = _warning;
197 once = TRUE;
198 }
199 f = g_fopen (info->filename, "rb");
200 if (!f)
201 return FALSE;
202 while ((n = fread (buffer, 1, BLOCKSIZE, f)) > 0) {
203 int result = xmlSAXUserParseMemory (&saxHandler, &ctx, buffer, n);
204 if (result != 0)
205 break;
206 if (ctx.state == READ_DONE)
207 break;
208 }
209 fclose (f);
210 if (ctx.state == READ_DONE) {
211 gchar* tmp = info->icon;
212 if (tmp) {
213 info->icon = custom_get_relative_filename (info->filename, tmp);
214 g_free (tmp);
215 }
216 return TRUE;
217 } else {
218 g_print ("Preloading shape file '%s' failed.\n"
219 "Please ensure that <name/> and <icon/> are early in the file.\n",
220 info->filename);
221 }
222 return FALSE;
223 }
224