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