* Still not quite right:
[scdataviz] / 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 #include <matdb-dotcode.h>
24 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <strings.h>
27 #include <stdlib.h>
28
29 void insert_into_matdb(struct matdb *mdb, struct matdb_material **mat, struct matdb_bowing **bow) {
30   if((*mat) != NULL) {
31 #ifdef DEBUG
32     fprintf(stderr, "inserting material %s\n", (*mat)->name->str);
33 #endif
34     g_hash_table_insert(mdb->materials, (gpointer)g_strdup((*mat)->name->str), (gpointer)(*mat));
35     (*mat) = NULL;
36   }
37   if((*bow) != NULL) {
38 #ifdef DEBUG
39     fprintf(stderr, "inserting bowing %s:%s\n", (*bow)->from->str, (*bow)->to->str);
40 #endif
41     g_hash_table_insert(mdb->bowings, (gpointer)g_strdup((*bow)->from->str), (gpointer)(*bow));
42     (*bow) = NULL;
43   }
44 }
45
46 /*Functions to be used by the hash.*/
47 static void destroy_string(gpointer data) {
48   free((char*)data);
49 }
50 static void destroy_double(gpointer data){
51   free((double*)data);
52 }
53
54 /*Removes leading and trailing whitespace, comments, and reduces
55   redundant whitespace down to one tab, makes an empty line an empty string.*/
56 static void prettify_line(char *line) {
57   register char *look;
58   register char *write;
59   register int in_whitespace;
60   register int leading_whitespace;
61   for(look=write=line, in_whitespace=0, leading_whitespace=1; *look != '\0'; look++) {
62 #ifdef DEBUG_2
63     fprintf(stderr, " look=%d(%c) write=%d(%c) in_whitespace=%d leading_whitespace=%d: ", (look-line), *look, (write-line), *write, in_whitespace, leading_whitespace);
64 #endif
65     if(*look == '#') {
66 #ifdef DEBUG_2
67       fprintf(stderr, "comment\n");
68 #endif
69       *write = '\0';
70       break;
71     }
72     switch(*look) {
73     case ' ':
74     case '\n':
75     case '\t':
76 #ifdef DEBUG_2
77       fprintf(stderr, "whitespace\n");
78 #endif
79       if((!leading_whitespace) && (!in_whitespace)) *write++ = '\t';
80       in_whitespace=1;
81       break;
82     default:
83 #ifdef DEBUG_2
84       fprintf(stderr, "default\n");
85 #endif
86       *write++ = *look;
87       in_whitespace=leading_whitespace=0;
88     }
89   }
90   *write='\0';
91   for(look=write=line; *look != '\0'; look++) {
92     switch(*look) {
93     case ' ':
94     case '\n':
95     case '\t':
96       if(*(look+1) == '\0') {
97         *look = '\0';
98       }
99     }
100   }
101 }
102
103 /*Error values:
104  *   0: success
105  *   1: failure malloc'ing / initializing mdb struct
106  *   2: failure opening file.
107  *   4: failure reading line.
108  *   8: warning reading line: no tab separator (line parsing error)
109  *  16: warning reading line: bad section declaration
110  *  32: warning reading line: unable to allocate material struct.
111  *  64: warning reading line: ignored line (unallocated material struct)
112  * 128: warning reading line: unable to allocate bowing struct
113  * 256: warning reading line: ignored line (unallocated bowing struct)
114  * 512: warning reading file: improper section detected (programming
115  *   error)
116  *1024: warning reading file: no : separator in bowing materials
117  *   description.
118  *2048: warning reading file: unable to read numeric value for property.
119  */
120 struct matdb* read_matdb_dotcode(const GString *name, int* err) {
121 #ifdef DEBUG_2
122   fprintf(stderr, "in read_matdb_dotcode(%s, %d)\n", name->str, *err);
123 #endif
124   *err=0;
125   struct matdb *mdb = (struct matdb*)malloc(sizeof(struct matdb));
126   if(mdb == NULL) {*err = 1; return NULL;}
127
128   double *value;
129   if((mdb->materials = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_material_gpointer)) == NULL) {
130     *err = 1;
131     free(mdb);
132     return NULL;
133   }
134   if((mdb->bowings = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_bowing_gpointer)) == NULL) {
135     *err = 1;
136     g_hash_table_unref(mdb->materials);
137     free(mdb);
138     return NULL;
139   }
140   struct matdb_material *mat = NULL;
141   struct matdb_bowing *bow = NULL;
142   /*Valid sections: 
143    * 0 (no/global section)
144    * 1 (material)
145    * 2 (bow)
146    */
147   int section=0;
148   int n;
149   char *line;
150   FILE *infile = fopen(name->str, "r");
151   if(infile == NULL) {
152     g_hash_table_unref(mdb->materials);
153     g_hash_table_unref(mdb->bowings);
154     free(mdb);
155     *err=2;
156     return NULL;
157 #ifdef DEBUG_2
158   }else{
159     fprintf(stderr, "infile=%x\n", (unsigned int)infile);
160 #endif
161   }
162   int val;
163   while(!feof(infile)) {
164 #ifdef DEBUG
165     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:"");
166 #endif
167     line=NULL;
168     if((val = getline(&line, &n, infile)) == -1) {
169 #ifdef DEBUG_2
170       fprintf(stderr, "getline returned %d\n", val); 
171 #endif
172       if(!feof(infile)) *err = 4;
173       //fclose(infile);
174       break;
175     }
176 #ifdef DEBUG_2
177     fprintf(stderr, "line=(%s)\n", line);
178 #endif
179     prettify_line(line);
180 #ifdef DEBUG
181     fprintf(stderr, "%d: prettified line=(%s)\n", section, line);
182 #endif
183     if(*line == '\0') {
184       free(line);
185       continue;
186     }
187     char *i = index(line, '\t');
188     if(i == NULL) {
189       *err &= 8;
190     }
191     *i++ = '\0';
192     /*At this point, we have line which stores the first word on the
193       line, and i which stores the second word on the line.
194     */
195     char *to;
196     GHashTable *ht;
197 #ifdef DEBUG_2
198     fprintf(stderr, "part_a=(%s) part_b=(%s)\n", line, i);
199 #endif
200     /*If we have a material or bowing underway, save it off*/
201     if(strcasecmp(line, "material") == 0) {
202       insert_into_matdb(mdb, &mat, &bow);
203       if((mat = (struct matdb_material*)malloc(sizeof(struct matdb_material))) == NULL) {
204         *err &= 32;
205       }
206       if((mat->name = g_string_new(i)) == NULL) {
207         *err &= 32;
208         free(mat);
209         section=0;
210         continue;
211       }
212       if((mat->properties = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_double)) == NULL) {
213         *err &= 32;
214         g_string_free(mat->name, TRUE);
215         free(bow);
216         section=0;
217         continue;
218       }
219 #ifdef DEBUG_2
220       fprintf(stderr, "new material (%s):\n", i);
221 #endif
222       section=1;
223     }else if(strcasecmp(line, "bow") == 0) {
224       insert_into_matdb(mdb, &mat, &bow);
225       if((bow = (struct matdb_bowing*)malloc(sizeof(struct matdb_bowing))) == NULL) {
226         *err &= 128;
227       }
228       if((to = index(i, ':')) == NULL) {
229         *err &= 1024;
230         free(bow);
231         section=0;
232         continue;
233       }
234       *to++ = '\0';
235       /*Same trick as before, but i now stores the from material,
236         and to the to material
237       */
238       if((bow->from = g_string_new(i)) == NULL) {
239         *err &= 128;
240         free(bow);
241         section=0;
242         continue;
243       }
244       if((bow->to = g_string_new(to)) == NULL) {
245         *err &= 128;
246         g_string_free(bow->from, TRUE);
247         free(bow);
248         section=0;
249         continue;
250       }
251       if((bow->properties = g_hash_table_new_full(&g_str_hash, &g_str_equal, &destroy_string, &destroy_double)) == NULL) {
252         *err &= 128;
253         g_string_free(bow->to, TRUE);
254         g_string_free(bow->from, TRUE);
255         free(bow);
256         section=0;
257         continue;
258       }
259 #ifdef DEBUG_2
260       fprintf(stderr, "new bowing (%s:%s):\n", i, to);
261 #endif
262       section=2;
263     }else{
264       /*Process a property.*/
265       switch(section) {
266       case 1:
267         ht = mat->properties;
268         break;
269       case 2:
270         ht = bow->properties;
271         break;
272       default:
273         *err &= 16;
274         section = 0;
275         continue;
276       }
277       if((value = (double*)malloc(sizeof(double))) == NULL) {
278         *err &= 2048;
279         continue;
280       }
281       if(sscanf(" %g", i, value) != 1) {
282         *err &= 2048;
283         continue;
284       }
285       g_hash_table_insert(ht, (gpointer)g_strdup(i), (gpointer)value);
286 #ifdef DEBUG_2
287       fprintf(stderr, "\t%d/%s\t%g(%s)\n", section, line, *value, i);
288 #endif
289     }
290     free(line);
291     line=i=to=NULL;
292     value=NULL;
293   }
294   fclose(infile);
295   insert_into_matdb(mdb, &mat, &bow);
296   return mdb;
297 }
298