* configure.ac:
[scdataviz] / src / matdb-dotcode.c
1 /*
2  * matdb-dotcode: reads in Craig's Dotcode materials database.
3
4
5
6  Copyright (C) 2008 Joseph Pingenot
7
8  This program is free software: you can redistribute it and/or modify
9  it under the terms of the GNU Affero General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU Affero General Public License for more details.
17
18  You should have received a copy of the GNU Affero General Public License
19  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #define _GNU_SOURCE
24 #include <stdio.h>
25 #include <strings.h>
26 #include <stdlib.h>
27 #include "config.h"
28 #include "matdb-dotcode.h"
29
30 #ifdef DEBUG
31 #undef DEBUG
32 #endif
33 #ifdef DEBUG_2
34 #undef DEBUG_2
35 #endif
36
37 /*Functions to be used by the hash.*/
38 static void destroy_string(gpointer data) {
39   free((char*)data);
40 }
41 static void destroy_double(gpointer data){
42   free((double*)data);
43 }
44 static void destroy_inner_bowhash(gpointer data) {
45   g_hash_table_unref(data);
46 }
47
48 int insert_into_matdb(struct matdb *mdb, struct matdb_material **mat, struct matdb_bowing **bow) {
49   if((*mat) != NULL) {
50 #ifdef DEBUG
51     fprintf(stderr, "inserting material %s\n", (*mat)->name->str);
52 #endif
53     g_hash_table_insert(mdb->materials, (gpointer)g_strdup((*mat)->name->str), (gpointer)(*mat));
54     (*mat) = NULL;
55   }
56   if((*bow) != NULL) {
57 #ifdef DEBUG
58     fprintf(stderr, "inserting bowing %s:%s\n", (*bow)->from->str, (*bow)->to->str);
59 #endif
60     /*Inner table*/
61     GHashTable *it;
62     if((it=g_hash_table_lookup(mdb->bowings, (*bow)->from->str)) == NULL) {
63       if((it = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_inner_bowhash)) == NULL) {
64         (*bow) = NULL;
65         return 1;
66       }
67       g_hash_table_insert(mdb->bowings, (*bow)->from->str, it);
68       #ifdef DEBUG
69       fprintf(stderr, "Got new hash table (from=%s)\n", (*bow)->from->str);
70       #endif
71     }
72     g_hash_table_insert(it, (gpointer)g_strdup((*bow)->to->str), (gpointer)(*bow));
73     (*bow) = NULL;
74   }
75   return 0;
76 }
77
78 /*Removes leading and trailing whitespace, comments, and reduces
79   redundant whitespace down to one tab, makes an empty line an empty string.*/
80 static void prettify_line(char *line) {
81   register char *look;
82   register char *write;
83   register int in_whitespace;
84   register int leading_whitespace;
85   for(look=write=line, in_whitespace=0, leading_whitespace=1; *look != '\0'; look++) {
86 #ifdef DEBUG_2
87     fprintf(stderr, " look=%d(%c) write=%d(%c) in_whitespace=%d leading_whitespace=%d: ", (look-line), *look, (write-line), *write, in_whitespace, leading_whitespace);
88 #endif
89     if(*look == '#') {
90 #ifdef DEBUG_2
91       fprintf(stderr, "comment\n");
92 #endif
93       *write = '\0';
94       break;
95     }
96     switch(*look) {
97     case ' ':
98     case '\n':
99     case '\t':
100 #ifdef DEBUG_2
101       fprintf(stderr, "whitespace\n");
102 #endif
103       if((!leading_whitespace) && (!in_whitespace)) *write++ = '\t';
104       in_whitespace=1;
105       break;
106     default:
107 #ifdef DEBUG_2
108       fprintf(stderr, "default\n");
109 #endif
110       *write++ = *look;
111       in_whitespace=leading_whitespace=0;
112     }
113   }
114   *write='\0';
115   for(look=write=line; *look != '\0'; look++) {
116     switch(*look) {
117     case ' ':
118     case '\n':
119     case '\t':
120       if(*(look+1) == '\0') {
121         *look = '\0';
122       }
123     }
124   }
125 }
126
127 /*Error values:
128  *   0: success
129  *   1: failure malloc'ing / initializing mdb struct
130  *   2: failure opening file.
131  *   4: failure reading line.
132  *   8: warning reading line: no tab separator (line parsing error)
133  *  16: warning reading line: bad section declaration
134  *  32: warning reading line: unable to allocate material struct.
135  *  64: warning reading line: ignored line (unallocated material struct)
136  * 128: warning reading line: unable to allocate bowing struct
137  * 256: warning reading line: ignored line (unallocated bowing struct)
138  * 512: warning reading file: improper section detected (programming
139  *   error)
140  *1024: warning reading file: no : separator in bowing materials
141  *   description.
142  *2048: warning reading file: unable to read numeric value for property.
143  */
144 struct matdb* read_matdb_dotcode(const GString *name, int* err) {
145 #ifdef DEBUG_2
146   fprintf(stderr, "in read_matdb_dotcode(%s, %d)\n", name->str, *err);
147 #endif
148   *err=0;
149   struct matdb *mdb = (struct matdb*)malloc(sizeof(struct matdb));
150   if(mdb == NULL) {*err = 1; return NULL;}
151
152   double *value;
153   if((mdb->materials = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_material_gpointer)) == NULL) {
154     *err = 1;
155     free(mdb);
156     return NULL;
157   }
158   if((mdb->bowings = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_bowing_gpointer)) == NULL) {
159     *err = 1;
160     g_hash_table_unref(mdb->materials);
161     free(mdb);
162     return NULL;
163   }
164   struct matdb_material *mat = NULL;
165   struct matdb_bowing *bow = NULL;
166   /*Valid sections: 
167    * 0 (no/global section)
168    * 1 (material)
169    * 2 (bow)
170    */
171   int section=0;
172   int n;
173   char *line;
174   FILE *infile = fopen(name->str, "r");
175   if(infile == NULL) {
176     g_hash_table_unref(mdb->materials);
177     g_hash_table_unref(mdb->bowings);
178     free(mdb);
179     *err=2;
180     return NULL;
181 #ifdef DEBUG_2
182   }else{
183     fprintf(stderr, "infile=%x\n", (unsigned int)infile);
184 #endif
185   }
186   int val;
187   while(!feof(infile)) {
188 #ifdef DEBUG
189     fprintf(stderr, "mat=%x(%s), bow=%x(%s:%s)\n", (unsigned int)mat, mat?mat->name->str:"", (unsigned int)bow, bow?bow->from->str:"", bow?bow->to->str:"");
190 #endif
191     line=NULL;
192     if((val = getline(&line, &n, infile)) == -1) {
193 #ifdef DEBUG_2
194       fprintf(stderr, "getline returned %d\n", val); 
195 #endif
196       if(!feof(infile)) *err = 4;
197       //fclose(infile);
198       break;
199     }
200 #ifdef DEBUG_2
201     fprintf(stderr, "line=(%s)\n", line);
202 #endif
203     prettify_line(line);
204 #ifdef DEBUG
205     fprintf(stderr, "%d: prettified line=(%s)\n", section, line);
206 #endif
207     if(*line == '\0') {
208       free(line);
209       continue;
210     }
211     char *i = index(line, '\t');
212     if(i == NULL) {
213       *err &= 8;
214     }
215     *i++ = '\0';
216     /*At this point, we have line which stores the first word on the
217       line, and i which stores the second word on the line.
218     */
219     char *to;
220     GHashTable *ht;
221 #ifdef DEBUG_2
222     fprintf(stderr, "part_a=(%s) part_b=(%s)\n", line, i);
223 #endif
224     /*If we have a material or bowing underway, save it off*/
225     if(strcasecmp(line, "material") == 0) {
226       insert_into_matdb(mdb, &mat, &bow);
227       if((mat = (struct matdb_material*)malloc(sizeof(struct matdb_material))) == NULL) {
228         *err &= 32;
229       }
230       if((mat->name = g_string_new(i)) == NULL) {
231         *err &= 32;
232         free(mat);
233         section=0;
234         continue;
235       }
236       if((mat->properties = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_double)) == NULL) {
237         *err &= 32;
238         g_string_free(mat->name, TRUE);
239         free(bow);
240         section=0;
241         continue;
242       }
243 #ifdef DEBUG_2
244       fprintf(stderr, "new material (%s):\n", i);
245 #endif
246       section=1;
247     }else if(strcasecmp(line, "bow") == 0) {
248       insert_into_matdb(mdb, &mat, &bow);
249       if((bow = (struct matdb_bowing*)malloc(sizeof(struct matdb_bowing))) == NULL) {
250         *err &= 128;
251       }
252       if((to = index(i, ':')) == NULL) {
253         *err &= 1024;
254         free(bow);
255         section=0;
256         continue;
257       }
258       *to++ = '\0';
259       /*Same trick as before, but i now stores the from material,
260         and to the to material
261       */
262       if((bow->from = g_string_new(i)) == NULL) {
263         *err &= 128;
264         free(bow);
265         section=0;
266         continue;
267       }
268       if((bow->to = g_string_new(to)) == NULL) {
269         *err &= 128;
270         g_string_free(bow->from, TRUE);
271         free(bow);
272         section=0;
273         continue;
274       }
275       if((bow->properties = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_double)) == NULL) {
276         *err &= 128;
277         g_string_free(bow->to, TRUE);
278         g_string_free(bow->from, TRUE);
279         free(bow);
280         section=0;
281         continue;
282       }
283 #ifdef DEBUG_2
284       fprintf(stderr, "new bowing (%s:%s):\n", i, to);
285 #endif
286       section=2;
287     }else{
288       #ifdef DEBUG
289       fprintf(stderr, "\t%d/%s\t", section, line);
290       #endif
291       /*Process a property.*/
292       switch(section) {
293       case 1:
294         #ifdef DEBUG
295         fprintf(stderr, "(1)\t");
296         #endif
297         ht = mat->properties;
298         break;
299       case 2:
300         #ifdef DEBUG
301         fprintf(stderr, "(2)\t");
302         #endif
303         ht = bow->properties;
304         break;
305       default:
306         #ifdef DEBUG
307         fprintf(stderr, "(default)\t");
308         #endif
309         *err &= 16;
310         section = 0;
311       }
312       if(section == 0) {
313         #ifdef DEBUG
314         fprintf(stderr, "section was 0\n");
315         #endif
316         continue;
317       }
318       if((value = (double*)malloc(sizeof(double))) == NULL) {
319         *err &= 2048;
320         #ifdef DEBUG
321         fprintf(stderr, "malloc of value failed\n");
322         #endif
323         continue;
324       }
325       int num;
326       if((num=sscanf(i, " %lg", value)) != 1) {
327         *err &= 2048;
328         free(value);
329         #ifdef DEBUG
330         fprintf(stderr, "bad sscanf to get value (scanned \"%s\", returned %d)\n", i, num);
331         #endif
332         continue;
333       }
334       g_hash_table_insert(ht, g_strdup(line), value);
335 #ifdef DEBUG
336       fprintf(stderr, "%g(%s)\n", *value, line);
337 #endif
338     }
339     free(line);
340     line=i=to=NULL;
341     value=NULL;
342   }
343   fclose(infile);
344   insert_into_matdb(mdb, &mat, &bow);
345   return mdb;
346 }
347