|
gimme-alpha 0.1
|
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 }