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