gimme-alpha 0.1

textfile_common.c

Go to the documentation of this file.
00001 /*
00002 ** textfile_common.c
00003 ** 
00004 ** Made by (Johnny Q. Hacker)
00005 ** Login   <solarion@borkborkbork>
00006 ** 
00007 
00008     Copyright (C) 2008 Joseph Pingenot
00009 
00010     This program is free software: you can redistribute it and/or modify
00011     it under the terms of the GNU Affero General Public License as published by
00012     the Free Software Foundation, either version 3 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU Affero General Public License for more details.
00019 
00020     You should have received a copy of the GNU Affero General Public License
00021     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 
00023 ** Started on  Mon Sep 29 16:22:43 2008 Johnny Q. Hacker
00024 ** Last update Wed Oct  1 09:47:52 2008 Johnny Q. Hacker
00025 */
00026 
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <stdio.h>
00030 #include "gimme_alpha_types.h"
00031 #include "textfile_common.h"
00032 #include "gcc_hints.h"
00033 
00034 /*Removes leading and trailing whitespace, comments, and reduces
00035   redundant whitespace down to one tab, makes an empty line an empty string.*/
00036 hotfunc void prettify_line(char *line, char comment) {
00037   register char *look;
00038   register char *write;
00039   register int in_whitespace;
00040   register int leading_whitespace;
00041   for(look=write=line, in_whitespace=0, leading_whitespace=1; *look != '\0'; look++) {
00042 #ifdef DEBUG_2
00043     fprintf(stderr, " look=%d(%c) write=%d(%c) in_whitespace=%d leading_whitespace=%d: ", (look-line), *look, (write-line), *write, in_whitespace, leading_whitespace);
00044 #endif
00045     if(*look == comment) {
00046 #ifdef DEBUG_2
00047       fprintf(stderr, "comment\n");
00048 #endif
00049       *write = '\0';
00050       break;
00051     }
00052     switch(*look) {
00053     case ' ':
00054     case '\n':
00055     case '\r':
00056     case '\t':
00057 #ifdef DEBUG_2
00058       fprintf(stderr, "whitespace\n");
00059 #endif
00060       if((!leading_whitespace) && (!in_whitespace)) *write++ = '\t';
00061       in_whitespace=1;
00062       break;
00063     default:
00064 #ifdef DEBUG_2
00065       fprintf(stderr, "default\n");
00066 #endif
00067       *write++ = *look;
00068       in_whitespace=leading_whitespace=0;
00069     }
00070   }
00071   *write='\0';
00072   for(look=write=line; *look != '\0'; look++) {
00073     switch(*look) {
00074     case ' ':
00075     case '\n':
00076     case '\t':
00077       if(*(look+1) == '\0') {
00078   *look = '\0';
00079       }
00080     }
00081   }
00082 }
00083 
00084 static void print_list(gpointer s, gpointer u) {
00085   printf("\t\t(%s)\n", (char*)s);
00086 }
00087 static void print_lists(gpointer s, gpointer u) {
00088   g_list_foreach(g_list_first(s), print_list, u);
00089   if(s == NULL) printf("[null list]\n");
00090   printf("\n");
00091 }
00092 
00093 /*Use strtok to split this string into a sublist*/
00094 static void split_list_into_sublist(gpointer s, gpointer listlist) {
00095   GList *slist = NULL;
00096   GList **ll = (GList **)listlist;
00097   char *p;
00098   char *saveptr = NULL;
00099   //fprintf(stderr, "\tsplit_list_into_sublist: listlist=%p(%p), s=(%s)\n", listlist, (listlist==NULL)?NULL:(*ll), (char*)s);
00100   slist = g_list_append(slist, strtok_r((char*)s, "\t", &saveptr));
00101   while((p=strtok_r(NULL, "\t", &saveptr)) != NULL) {
00102     //fprintf(stderr, "\t\tp=(%s)\n", p);
00103     slist = g_list_append(slist, p);
00104   }
00105   *ll = g_list_append(*ll, slist);
00106 }
00107 
00108 /*Simple: if the key is not NULL, add it to the front of this list.
00109  *Then take the last item from our list and set key to it.
00110  *RETURNS:
00111  *  The new head of the sublist in list.
00112  */
00113 static void pop_n_push_keys(void** l, GList** k) {
00114   if(unlikely((l == NULL) || (k == NULL))) return;
00115   GList **list = (GList**)l;
00116   /*
00117   fprintf(stderr, "\tpop_n_push_keys: list=%p(%p) key=%p(%p)\n", list, *list, k, *k);
00118   fprintf(stderr, "\t\tList status:\n");
00119   if(*list == NULL) {
00120     fprintf(stderr, "\t\t(nil)\n");
00121   }else{ 
00122     g_list_foreach(*list, print_list, NULL);
00123   }
00124   */
00125   if((*k) != NULL) {
00126     //fprintf(stderr, "\t\tkey=%s\n", (char*)((*k)->data));
00127     *list = g_list_concat(*k, g_list_first(*list));
00128     //fprintf(stderr, "\t\tafter prepend: list=%p key=%p (%s)\n", *list, *k, (char*)((*k)->data));
00129   }
00130   /*
00131   fprintf(stderr, "\t\tList status:\n");
00132   if(*list == NULL) {
00133     fprintf(stderr, "\t\t(nil)\n");
00134   }else{ 
00135     g_list_foreach(*list, print_list, NULL);
00136   }
00137   */
00138   *k = g_list_last(*list);
00139   /*
00140   fprintf(stderr, "\t\tafter setting k: list=%p key=%p (%s)\n", *list, *k, (char*)((*k)->data));
00141   fprintf(stderr, "\t\tList status:\n");
00142   if(*list == NULL) {
00143     fprintf(stderr, "\t\t(nil)\n");
00144   }else{ 
00145     g_list_foreach(*list, print_list, NULL);
00146   }
00147   */
00148   *list = g_list_remove_link(*list, *k);
00149   /*
00150   fprintf(stderr, "\t\tafter removing k: list=%p key=%p (%s)\n", *list, *k, (char*)((*k)->data));
00151   fprintf(stderr, "\t\tList status:\n");
00152   if(*list == NULL) {
00153     fprintf(stderr, "\t\t(nil)\n");
00154   }else{ 
00155     g_list_foreach(*list, print_list, NULL);
00156   }
00157   */
00158 }
00159 
00160 /*gets all of the key/value pairs on a line.
00161  *ARGS:
00162  *  s the string to parse
00163  *  delim key/value delimiter(s)
00164  *  space space character(s)
00165  *RETURNS:
00166  *  A list of list of key/value pairs, with the first entry in each
00167  *being the key for that value.
00168  *  The *first* list in the list returned, however, is any initial
00169  *values that may be present on the line (i.e. values which came
00170  *before the first key).  This may be (and hopefully is) an empty list.
00171  */
00172 GList *get_key_value_pairs(char* s, const char *delim) {
00173   GList *l = NULL;
00174   GList *slist = NULL;
00175   char *p;
00176   char *saveptr;
00177   //fprintf(stderr, "get_key_value_pairs: got string (%s) and delim=(%s)\n", s, delim);
00178   slist = g_list_append(slist, strtok_r(s, delim, &saveptr));
00179   while((p=strtok_r(NULL, delim, &saveptr)) != NULL) {
00180     slist = g_list_append(slist, p);
00181   }
00182   g_list_foreach(slist, split_list_into_sublist, &l);
00183   GList *key = NULL;
00184   /*
00185   fprintf(stderr, "\tafter splitting into sublists:\n");
00186   g_list_foreach(l, print_lists, NULL);
00187   */
00188   GList *i;
00189   for(i = g_list_first(l); i != NULL; i = g_list_next(i)) {
00190     pop_n_push_keys(&(i->data), &key);
00191   }
00192   g_list_last(l)->data = g_list_concat(g_list_first(g_list_last(l)->data), key);
00193   /*
00194   fprintf(stderr, "\tafter appending the popped key:\n");
00195   g_list_foreach(g_list_first(l), print_lists, NULL);
00196   */
00197   g_list_free(slist);
00198   return l;
00199 }
00200 
00201 static void free_if_not_null(gpointer data) {
00202   if(data != NULL) free(data);
00203 }
00204 
00205 
00206 /*Return a list contaning hashes with the column titles as keys.
00207  *ARGS:
00208  *  istream   input stream to read data from (the first line is the
00209  *              title line, which provides titles to be used in the
00210  *              hashes.  The columns are separated by whitespace.
00211  *              Multiple whitespaces are ignored (i.e. all columns
00212  *              MUST have a value in them.
00213  *  err       pointer to a pointer to hold any error that might occur
00214  *  titles    pointer to a pointer to a GList where the list of titles
00215  *              will be kept.  These are required to get the data out
00216  *              of the hashes.  If titles already exists, it will be
00217  *              used and the first line will be considered to be
00218  *              ordinary data.
00219  *  comment   the character that represents the start of a comment
00220  *RETURNS:
00221  *  a pointer to a GList of hashes which contain the values (double
00222  *     precision float); the keys are the respective column title.
00223  * OR
00224  *  NULL if reading failed.
00225  */
00226 GList* read_titled_whitespaced_data(GInputStream *istream, GError **err, GList **titles, char comment, enum data_types t) {
00227   GDataInputStream *in = g_data_input_stream_new(istream);
00228   GList *l = NULL, *slist=NULL;
00229   char *str, *p, *saveptr;
00230   GList *ct, *cs;
00231   int got_title=0;
00232   GHashTable *h=NULL;
00233   gsize len;
00234   //void *ptr;
00235 
00236   if(*titles != NULL) {
00237     got_title=1;
00238     /*We have to invert the list because it needs to be inverted for
00239       the later logic. and also because it'll be re-inverted at the end.*/
00240     *titles = g_list_reverse(*titles);
00241   }
00242 
00243   /*Note: although ordering will be reversed (for speed) from what's
00244    * in the file, since both title and row lists are reversed, they
00245    * work out to be the same.  Ordering in the hash is irrelevant.
00246    * However, beacuse the user probably wants the titles to be in the
00247    * same order as in the file, we'll reverse the order of that before
00248    * returning.
00249    */
00250   int i = -1;;
00251   while((str = g_data_input_stream_read_line(in, &len, NULL, err)) != NULL) {
00252     //fprintf(stderr, "got line: %s\n", str);
00253     prettify_line(str, comment);
00254     if((strlen(str)) == 0) continue;
00255     slist = g_list_prepend(NULL, strtok_r(str, "\t", &saveptr));
00256     while((p=strtok_r(NULL, "\t", &saveptr)) != NULL) {
00257       slist = g_list_prepend(slist, p);
00258     }
00259     if(unlikely(!got_title)) {
00260       //fprintf(stderr, "Got titles\n");
00261       *titles = slist;
00262       got_title=1;
00263     }else{
00264       i++;
00265       //fprintf(stderr, "Got row\n");
00266       h = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_if_not_null);
00267       //fprintf(stderr, "%d\t", i);
00268       for(ct=g_list_first(*titles), cs=g_list_first(slist); 
00269     (ct != NULL) && (cs != NULL); 
00270     ct = g_list_next(ct), cs=g_list_next(cs)) {
00271   /*
00272   ptr = string_to_data(cs->data, t);
00273   fprintf(stderr, "\tdata=%s\tp=%p\ttitle=%s\n", (char*)cs->data, ptr, (char*)ct->data);
00274   g_hash_table_replace(h, ct->data, ptr);
00275   char s[128];
00276   sprintf(s, "%%s -> %s\t", format_string_for_type_print(t));
00277   fprintf(stderr, s, cs->data,
00278   *(double*)string_to_data(cs->data, t));
00279   */
00280   g_hash_table_replace(h, ct->data, string_to_data(cs->data, t));
00281       }
00282       //fprintf(stderr, "\n");
00283       l = g_list_prepend(l, h);
00284       h = NULL;
00285       //Free the temporary row storage list.
00286       g_list_free(slist);
00287       if(unlikely((ct != NULL) || (cs != NULL))) {
00288   if(ct != NULL)
00289     fprintf(stderr, "WARNING: there are more titles than columns in this row (%s)!\n", str);
00290   else
00291     fprintf(stderr, "WARNING: there are more columns than titles in this row (%s)!\n", str);
00292       }
00293     }
00294   }
00295 
00296   /*Reverse the ordering of rows (they were prepended for performance)*/
00297   l = g_list_reverse(l);
00298   /*And also for titles.*/
00299   *titles = g_list_reverse(*titles);
00300   return l;
00301 }
 All Classes Files Functions Variables Enumerations Enumerator Defines