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