* Added dotcode matdb processor.
[scdataviz] / matdb-dotcode.c
diff --git a/matdb-dotcode.c b/matdb-dotcode.c
new file mode 100644 (file)
index 0000000..85560e4
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * matdb-dotcode: reads in Craig's Dotcode materials database.
+
+
+
+    Copyright (C) 2008 Joseph Pingenot
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <matdb-dotcode.h>
+#include <stdio.h>
+#include <strings.h>
+
+/*Removes leading and trailing whitespace, comments, and reduces
+  redundant whitespace down to one tab, makes an empty line an empty string.*/
+static void prettify_line(char *line) {
+  register char *look;
+  register char *write;
+  register int in_whitespace;
+  register int leading_whitespace;
+  for(look=write=line, in_whitespace=0, leading_whitespace=1; *look != '\0'; look++) {
+    switch(*look) {
+    case ' ':
+    case '\n':
+    case '\t':
+      if((!leading_whitespace) && (!in_whitespace)) *write++ = '\t';
+      in_whitespace=1;
+      break;
+    case '#':
+      *write='\0';
+      break;
+    default:
+      *write++ = *look;
+      in_whitespace=leading_whitespace=0;
+    }
+  }
+  for(look=write=line, *look != '\0'; look++) {
+    switch(*look) {
+    case ' ':
+    case '\n':
+    case '\t':
+      if(*(look+1) == '\0') break;
+      break;
+    default:
+      *write++ = *look;
+    }
+  }
+}
+
+/*Error values:
+ *   0: success
+ *   1: failure malloc'ing / initializing mdb struct
+ *   2: failure opening file.
+ *   4: failure reading line.
+ *   8: warning reading line: no tab separator (line parsing error)
+ *  16: warning reading line: bad section declaration
+ *  32: warning reading line: unable to allocate material struct.
+ *  64: warning reading line: ignored line (unallocated material struct)
+ * 128: warning reading line: unable to allocate bowing struct
+ * 256: warning reading line: ignored line (unallocated bowing struct)
+ * 512: warning reading file: improper section detected (programming
+ *   error)
+ *1024: warning reading file: no : separator in bowing materials
+ *   description.
+ *2048: warning reading file: unable to read numeric value for property.
+ */
+struct matdb* read_matdb_dotcode(const GString *name, int* err) {
+  *err=0;
+  struct matdb *mdb = (struct matdb*)malloc(sizeof(struct matdb));
+  if(mdb == NULL) {*err = 1; return NULL;}
+
+  double *value;
+  if((mdb->materials = g_ptr_array_new()) == NULL) {
+    *err = 1;
+    free(mdb);
+    return NULL;
+  }
+  if((mdb->bowings = g_ptr_array_new()) == NULL) {
+    *err = 1;
+    g_ptr_array_free(mdb->materials, TRUE);
+    free(mdb);
+    return NULL;
+  }
+  struct matdb_material *mat = NULL;
+  struct matdb_bowing *bow = NULL;
+  /*Valid sections: 
+   * 0 (no/global section)
+   * 1 (material)
+   * 2 (bow)
+   */
+  section=0;
+  char *line;
+  FILE *infile = fopen(name->str, "r");
+  if(infile == NULL) {
+    free(mdb);
+    *err=2;
+    return NULL;
+  }
+  while(!feof(infile)) {
+    line=NULL;
+    if(getline(&line, NULL, infile) == -1) {
+      if(!feof(infile)) *err = 4;
+      break;
+    }
+    #ifdef DEBUG
+    fprintf(stderr, "line=(%s)\n", line);
+    #endif
+    prettify_line(line);
+    #ifdef DEBUG
+    fprintf(stderr, "prettified line=(%s)\n", line);
+    #endif
+    if(*line == '\0') {
+      free(line);
+      continue;
+    }
+    char *i = index(line, '\t');
+    if(i == NULL) {
+      *err &= 8;
+    }
+    *i++ = '\0';
+    /*At this point, we have line which stores the first word on the
+      line, and i which stores the second word on the line.
+    */
+    char *to;
+    GHashTable *ht;
+    #ifdef DEBUG
+    fprintf(stderr, "part_a=(%s) part_b=(%s)\n", line, i);
+    #endif
+    if(section == 0) {
+      /*If we have a material or bowing underway, save it off*/
+      if(mat != NULL) {
+       g_ptr_array_add(mdb->materials, (gpointer)mat);
+       mat = NULL:
+      }
+      if(bow != NULL) {
+       g_ptr_array_add(mdb->bowings, (gpointer)bow);
+       bow = NULL:
+      }
+      if(strcasecmp(line, "material") == 0) {
+       if((mat = (struct *matdb_material)malloc(sizeof(struct matdb_material))) == NULL) {
+         *err &= 32;
+       }
+       if((mat->name = g_string_new(i)) == NULL) {
+         *err &= 32;
+         free(mat);
+         continue;
+       }
+       if((mat->properties = g_hash_table_new_full(g_str_hash, g_str_equal, &destroy_string, &destroy_double)) == NULL) {
+         *err &= 32;
+         g_string_free(mat->name, TRUE);
+         free(bow);
+         continue;
+       }
+#ifdef DEBUG
+       fprintf(stderr, "new material (%s):\n", i);
+#endif
+      }else if(strncasecmp(line, "bow") == 0) {
+       if((bow = (struct *matdb_bowing)malloc(sizeof(struct matdb_bowing))) == NULL) {
+         *err &= 128;
+       }
+       if((to = index(i, ':')) == NULL) {
+         *err &= 1024;
+         free(bow);
+         continue;
+       }
+       *to++ = '\0';
+       /*Same trick as before, but i now stores the from material,
+         and to the to material
+       */
+       if((bow->from = g_string_new(i)) == NULL) {
+         *err &= 128;
+         free(bow);
+         continue;
+       }
+       if((bow->to = g_string_new(to)) == NULL) {
+         *err &= 128;
+         g_string_free(bow->from, TRUE);
+         free(bow);
+         continue;
+       }
+       if((bow->properties = g_hash_table_new_full(g_str_hash, g_str_equal, &destroy_string, &destroy_double)) == NULL) {
+         *err &= 128;
+         g_string_free(bow->to, TRUE);
+         g_string_free(bow->from, TRUE);
+         free(bow);
+         continue;
+       }
+#ifdef DEBUG
+       fprintf(stderr, "new bowing (%s:%s):\n", i, to);
+#endif
+      }else{
+       *err &= 16;
+       section = 0;
+      }
+    }else{
+      /*Process a property.*/
+      switch(section) {
+      case: 1
+         ht = mat->properties;
+         break;
+      case: 2
+         ht = bow->properties;
+         break;
+      default:
+       *err &= 512;
+       section = 0;
+       continue;
+      }
+      if((value = (double*)malloc(sizeof(double))) == NULL) {
+       *err &= 2048;
+       continue;
+      }
+      if(sscanf(" %g", i, value) != 1) {
+       *err &= 2048;
+       continue;
+      }
+      g_hash_table_insert(ht, (gpointer)i, value);
+      #ifdef DEBUG
+      fprintf(stderr, "\t%d/%s\t%g(%s)\n", section, line, value, i);
+      #endif
+    }
+    free(line);
+    line=value=i=to=NULL;
+  }
+  fclose(file);
+  return mdb;
+}
+
+static void destroy_string(gpointer data) {
+  free((char*)data);
+}
+static void destroy_double(gpointer data){
+  free((double*)data);
+}