source: trunk/src/ruleset.c @ 168

Last change on this file since 168 was 168, checked in by simon, 5 years ago
  • Replace printf messages to stderr with g_critical
  • Fix identation
  • Property svn:eol-style set to native
  • Property svn:keywords set to "Date Author Id Revision HeadURL"
  • Property svn:mime-type set to text/x-csrc
File size: 13.1 KB
Line 
1/*
2 * Memphis - Cairo Rederer for OSM in C
3 * Copyright (C) 2008  Marius Rieder <marius.rieder@durchmesser.ch>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <glib.h>
21#include <glib/gstdio.h>
22#include <time.h>
23#include <expat.h>
24#include <string.h>
25
26#include "list.h"
27#include "ruleset.h"
28#include "mlib.h"
29#include "memphis-data-pool.h"
30
31#define BUFFSIZE 1024
32#define MAXDEPTH 20
33#define MAXSTACK 20
34
35typedef struct rulesUserData_ rulesUserData;
36struct rulesUserData_ {
37    // Pointers to work with
38    cfgRule *currentRule;
39    cfgRule *ruleStack[MAXSTACK];
40    char **stringStack;
41    MemphisDataPool *pool;
42    // Collected Data
43    cfgRules *ruleset;
44    // Debug
45    gint8 debug_level;
46};
47
48/**
49 * cfgStartElement:
50 * @userdata:   cfgRules struct to work with
51 * @name:       The element name
52 * @atts:       The element attributes
53 *
54 * Called when the start of an element has been detected.
55 */
56static void XMLCALL
57cfgStartElement(void *userData, const char *name, const char **atts) {
58    rulesUserData *data = (rulesUserData *)userData;
59    cfgRules *ruleset = data->ruleset;
60    GStringChunk *stringChunk = data->pool->stringChunk;
61    GTree *stringTree = data->pool->stringTree;
62    gint8 debug_level = data->debug_level;
63
64    if (debug_level > 1)
65        g_fprintf (stdout, "cfgStartElement\n");
66
67    // Parsing Rules
68    if (strcmp((char *) name, "rules") == 0) {
69        // Init Ruleset
70        ruleset->rule = NULL;
71
72        while (*atts != NULL) {
73            if(strcmp((char *) *(atts), "background") == 0) {
74              sscanf((char *) *(atts+1),"#%2x%2x%2x",
75                  (unsigned int *)&ruleset->background[0],
76                  (unsigned int *)&ruleset->background[1],
77                  (unsigned int *)&ruleset->background[2]);
78            }
79            atts+=2;
80        }
81    }
82    // Parsing Rule
83    else if (strcmp(name, "rule") == 0) {
84        ruleset->cntRule++;
85        ruleset->depth++;
86
87        // Create Rule
88        cfgRule *new;
89        new = g_new(cfgRule, 1);
90        new->next = NULL;
91        new->type = 0;
92        new->parent = NULL;
93        new->nparent = NULL;
94        new->draw = NULL;
95        new->ndraw = NULL;
96
97        int c;
98
99        // Populate Rule
100        while (*atts != NULL) {
101            if(strcmp((char *) *(atts), "e") == 0) {
102                if(strstr((char *) *(atts +1),"way") != NULL)
103                    new->type |= WAY;
104                if(strstr((char *) *(atts +1),"node") != NULL)
105                    new->type |= NODE;
106            } else if(strcmp((char *) *(atts), "k") == 0) {
107                new->key = g_strsplit((char *) *(atts + 1), "|", 0);
108                for(c = 0; c < g_strv_length(new->key); c++) {
109                    char *tmp = *(new->key + c);
110                    *(new->key + c) = m_string_chunk_get(stringChunk,
111                                                          stringTree, (char *) tmp);
112                    g_free(tmp);
113                }
114            } else if(strcmp((char *) *(atts), "v") == 0) {
115                new->value = g_strsplit((char *) *(atts + 1), "|", 0);
116                for(c = 0; c < g_strv_length(new->value); c++) {
117                    char *tmp = *(new->value + c);
118                    *(new->value + c) = m_string_chunk_get(stringChunk,
119                                                           stringTree, tmp);
120                    g_free(tmp);
121                }
122            }
123            atts += 2;
124        }
125
126        // Insert Rule to chain
127        if(ruleset->rule == NULL)
128            ruleset->rule = new;
129        else
130            data->currentRule->next = new;
131        data->currentRule = new;
132
133        // Adding to stack
134        data->ruleStack[ruleset->depth] = data->currentRule;
135
136    }
137    // Parsing Else
138    else if (strcmp(name, "else") == 0) {
139        ruleset->cntElse++;
140        ruleset->depth++;
141    }
142    // Parsing Polygone, etc.
143    else if (
144        strcmp(name, "polygone") == 0 ||
145        strcmp(name, "line") == 0 ||
146        strcmp(name, "text") == 0
147    ) {
148        // Create Draw
149        cfgDraw *new;
150        new = g_new(cfgDraw, 1);
151        new->pattern = NULL;
152        new->minzoom = 0;
153        new->maxzoom = 99;
154
155        // Populate Draw
156        if (strcmp(name, "polygone") == 0)
157            new->type = POLYGONE;
158        else if (strcmp(name, "line") == 0)
159            new->type = LINE;
160        else if (strcmp(name, "text") == 0)
161            new->type = TEXT;
162
163        while (*atts != NULL) {
164            if(strcmp((char *) *(atts), "color") == 0) {
165                sscanf((char *) *(atts+1),"#%2x%2x%2x",
166                                            (unsigned int *)&new->color[0],
167                                            (unsigned int *)&new->color[1],
168                                            (unsigned int *)&new->color[2]);
169            } else if(strcmp((char *) *(atts), "width") == 0) {
170                sscanf((char *) *(atts+1),"%f",&new->width);
171            } else if(strcmp((char *) *(atts), "pattern") == 0) {
172                new->pattern = m_string_chunk_get(stringChunk, stringTree,
173                                                  (char *) *(atts+1));
174            } else if(strcmp((char *) *(atts), "zoom") == 0) {
175                sscanf((char *) *(atts+1),"%hi:%hi",
176                                            &new->minzoom,
177                                            &new->maxzoom);
178            }
179            atts+=2;
180        }
181
182        // Insert Draw
183        if(data->currentRule->parent == 0)
184            LL_APPEND(new, data->ruleStack[ruleset->depth]->draw);
185        else
186            LL_APPEND(new, data->ruleStack[ruleset->depth]->ndraw);
187    }
188}
189
190/**
191 * cfgEndElement:
192 * @userdata:   cfgRules struct to work with
193 * @name:       The element name
194 *
195 * Called when the end of an element has been detected.
196 */
197static void XMLCALL
198cfgEndElement(void *userData, const char *name) {
199    rulesUserData *data = (rulesUserData *)userData;
200    cfgRules *ruleset = data->ruleset;
201    gint8 debug_level = data->debug_level;
202   
203    if (debug_level > 1)
204        g_fprintf (stdout, "cfgEndElement\n");
205
206    if (strcmp(name, "rule") == 0) {
207        // Fetching Parent from stack
208        if(ruleset->depth > 0) {
209            if (data->ruleStack[ruleset->depth-1]->parent == NULL) {
210                data->ruleStack[ruleset->depth]->parent = data->ruleStack[ruleset->depth-1];
211            } else {   // If parent already closed we are else.
212                data->ruleStack[ruleset->depth]->nparent = data->ruleStack[ruleset->depth-1];
213            }
214        }
215
216        ruleset->depth--;
217    } else if (strcmp(name, "else") == 0) {
218        ruleset->depth--;
219    }
220}
221
222/**
223 * rulesetRead:
224 * @filename:   Filename of the rule file
225 *
226 * Called to parse rules in the rulefile. Returns a cfgRules struct.
227 */
228cfgRules* rulesetRead(const char *filename, gint8 debug_level) {
229    if (debug_level > 1)
230        g_fprintf (stdout, "rulesetRead\n");
231
232    // Local Vars
233    GTimer *tRulesetRead = g_timer_new();
234    unsigned int size;
235    unsigned int read = 0;
236    struct stat filestat;
237    int         len;
238    int         done;
239    char        *buf;
240    rulesUserData *data = g_new(rulesUserData, 1);
241    cfgRules    *ruleset = NULL;
242
243    // NULL rule stack
244    for (len=0;len<MAXDEPTH;len++) {
245        data->ruleStack[len] = NULL;
246    }
247   
248    // Test file
249    if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
250        g_critical ("Error: \"%s\" is not a file.", filename);
251        return NULL;
252    }
253   
254    g_stat(filename, &filestat);
255    size = (int) filestat.st_size;
256
257    // Open file
258    FILE *fd = fopen(filename,"r");
259    if(fd == NULL) {
260        g_critical ("Error: Can't open file \"%s\"\n", filename);
261        return NULL;
262    }
263
264    ruleset = g_new(cfgRules, 1);
265    ruleset->depth = -1;
266    ruleset->cntRule = 0;
267    ruleset->cntElse = 0;
268    data->ruleset = ruleset;
269    data->pool = memphis_data_pool_new ();
270    data->debug_level = debug_level;
271
272    if (debug_level > 0) {
273        g_fprintf(stdout, " Ruleset parsing   0%%");
274        fflush(stdout);
275    }
276
277    // Create XML Parser
278    XML_Parser parser = XML_ParserCreate(NULL);
279    XML_SetElementHandler(parser, cfgStartElement, cfgEndElement);
280
281    XML_SetUserData(parser, data);
282
283    // Create Buffer
284    buf = g_malloc(BUFFSIZE*sizeof(char));
285
286    // Looping over XML
287    while(!feof(fd)) {
288        len = (int)fread(buf, 1, BUFFSIZE, fd);
289        if (ferror(fd)) {
290            g_critical("Ruleset read error");
291            // cleanup
292            XML_ParserFree(parser);
293            g_free(buf);
294            fclose(fd);
295            g_free(data);
296            g_free(ruleset);
297            return NULL;
298        }
299        read += len;
300        if (debug_level > 0) {
301            g_fprintf(stdout, "\r Ruleset parsing % 3i%%", (int)((read*100)/size));
302            fflush(stdout);
303        }
304        done = len < sizeof(buf);
305        if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
306            g_critical ("Parse error at line %i: %s",
307                    (int) XML_GetCurrentLineNumber(parser),
308                    XML_ErrorString(XML_GetErrorCode(parser)));
309            // cleanup
310            XML_ParserFree(parser);
311            g_free(buf);
312            fclose(fd);
313            g_free(data);
314            g_free(ruleset);
315            return NULL;
316        }
317    }
318
319    // Cleaning Memory
320    XML_ParserFree(parser);
321    g_free(buf);
322    fclose(fd);
323    g_free(data);
324
325    if (debug_level > 0)
326        g_fprintf (stdout, "\r Ruleset parsing done. (%i/%i) [%fs]\n",
327                ruleset->cntRule,  ruleset->cntElse,
328                g_timer_elapsed(tRulesetRead,NULL));
329   
330    g_timer_destroy(tRulesetRead);
331
332    return ruleset;
333}
334
335/**
336 * rulesetRead_from_buffer:
337 * @buffer: Buffer containing a rule-set
338 * @size: Size of the buffer
339 *
340 * Called to parse rules in a buffer. Returns a cfgRules struct.
341 */
342cfgRules* rulesetRead_from_buffer (const char *buffer, guint size, gint8 debug_level) {
343    if (debug_level > 1)
344        g_fprintf (stdout, "rulesetRead\n");
345
346    g_assert (buffer != NULL && size > 0);
347
348    // Local Vars
349    GTimer *tRulesetRead = g_timer_new();
350    int         len;
351    int         isDone = 0;
352    rulesUserData *data = g_new(rulesUserData, 1);
353    cfgRules    *ruleset = NULL;
354
355    // NULL rule stack
356    for (len=0;len<MAXDEPTH;len++) {
357        data->ruleStack[len] = NULL;
358    }
359
360    ruleset = g_new(cfgRules, 1);
361    ruleset->depth = -1;
362    ruleset->cntRule = 0;
363    ruleset->cntElse = 0;
364    data->ruleset = ruleset;
365    data->pool = memphis_data_pool_new ();
366    data->debug_level = debug_level;
367
368    if (debug_level > 0) {
369        g_fprintf(stdout, " Ruleset parsing   0%%");
370        fflush(stdout);
371    }
372
373    // Create XML Parser
374    XML_Parser parser = XML_ParserCreate(NULL);
375    XML_SetElementHandler(parser, cfgStartElement, cfgEndElement);
376    XML_SetUserData(parser, data);
377
378    // Parse the buffer
379    if (XML_Parse (parser, buffer, size, isDone) == XML_STATUS_ERROR) {
380        g_critical ("Parse error at line %i: %s",
381                (int) XML_GetCurrentLineNumber(parser),
382                XML_ErrorString(XML_GetErrorCode(parser)));
383        // cleanup
384        XML_ParserFree(parser);
385        g_free(data);
386        g_free(ruleset);
387        return NULL;
388    }
389
390    // Cleaning Memory
391    XML_ParserFree(parser);
392    g_free(data);
393
394    if (debug_level > 0)
395        g_fprintf (stdout, "\r Ruleset parsing done. (%i/%i) [%fs]\n",
396                ruleset->cntRule,  ruleset->cntElse,
397                g_timer_elapsed(tRulesetRead,NULL));
398
399    g_timer_destroy(tRulesetRead);
400
401    return ruleset;
402}
403
404void rulesetFree(cfgRules * ruleset) {
405    cfgRule *rule, *lrule;
406    cfgDraw *draw, *ldraw;
407
408    for(rule = ruleset->rule, lrule = NULL;
409        rule != NULL;
410        lrule = rule, rule = rule->next)
411    {
412        for(draw = rule->draw, ldraw = NULL;
413            draw != NULL;
414            ldraw = draw, draw = draw->next)
415        {
416            if(ldraw)
417                g_free(ldraw);
418        }
419        if(ldraw)
420            g_free(ldraw);
421        for(draw = rule->ndraw, ldraw = NULL;
422            draw != NULL;
423            ldraw = draw, draw = draw->next)
424        {
425            if(ldraw)
426                g_free(ldraw);
427        }
428        if(ldraw)
429            g_free(ldraw);
430
431        g_free(rule->key);
432        g_free(rule->value);
433
434        if(lrule)
435            g_free(lrule);
436    }
437    g_free(lrule);
438    g_free(ruleset);
439}
440
441void cfgRuleFree (cfgRule *rule)
442{
443    g_free (rule->key);
444    g_free (rule->value);
445
446    cfgDraw *tmp;
447    cfgDraw *drw = rule->draw;
448    while (drw != NULL)
449    {
450        tmp = drw;
451        drw = drw->next;
452        g_free (tmp);
453    }
454
455    g_free (rule);
456}
457
458/*
459 * vim: expandtab shiftwidth=4 tabstop=4:
460 */
461
Note: See TracBrowser for help on using the repository browser.