|
|
1.1 ! root 1: /* dat_file.c */ ! 2: ! 3: /* Functions to deal with comma (CSV) and tab-delimited files and lists */ ! 4: ! 5: /* $Id: dat_file.c,v 1.5 2006/08/14 22:55:48 rswindell Exp $ */ ! 6: ! 7: /**************************************************************************** ! 8: * @format.tab-size 4 (Plain Text/Source Code File Header) * ! 9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * ! 10: * * ! 11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * ! 12: * * ! 13: * This library is free software; you can redistribute it and/or * ! 14: * modify it under the terms of the GNU Lesser General Public License * ! 15: * as published by the Free Software Foundation; either version 2 * ! 16: * of the License, or (at your option) any later version. * ! 17: * See the GNU Lesser General Public License for more details: lgpl.txt or * ! 18: * http://www.fsf.org/copyleft/lesser.html * ! 19: * * ! 20: * Anonymous FTP access to the most recent released source is available at * ! 21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * ! 22: * * ! 23: * Anonymous CVS access to the development source and modification history * ! 24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: * ! 25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login * ! 26: * (just hit return, no password is necessary) * ! 27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src * ! 28: * * ! 29: * For Synchronet coding style and modification guidelines, see * ! 30: * http://www.synchro.net/source.html * ! 31: * * ! 32: * You are encouraged to submit any modifications (preferably in Unix diff * ! 33: * format) via e-mail to [email protected] * ! 34: * * ! 35: * Note: If this box doesn't appear square, then you need to fix your tabs. * ! 36: ****************************************************************************/ ! 37: ! 38: #include "dat_file.h" ! 39: #include "genwrap.h" /* lastchar */ ! 40: #include "filewrap.h" /* chsize */ ! 41: #include <stdlib.h> /* malloc */ ! 42: #include <string.h> /* strdup */ ! 43: ! 44: /***********************************/ ! 45: /* CSV (Comma Separated Value) API */ ! 46: /***********************************/ ! 47: ! 48: static char* csvEncode(char* field) ! 49: { ! 50: char* dst; ! 51: char* src; ! 52: char* buf; ! 53: char* comma; ! 54: char* quote; ! 55: char first; ! 56: char last; ! 57: char* nl; ! 58: BOOL enclose; ! 59: ! 60: if((buf=malloc(strlen(field)*2))==NULL) ! 61: return(NULL); ! 62: ! 63: nl=strchr(field,'\n'); ! 64: comma=strchr(field,','); ! 65: quote=strchr(field,'"'); ! 66: first=field[0]; ! 67: last=*lastchar(field); ! 68: ! 69: enclose = (quote || comma || nl || first==' ' || last==' '); ! 70: ! 71: dst=buf; ! 72: if(enclose) ! 73: *(dst++)='"'; ! 74: src=field; ! 75: while(*src) { ! 76: if(*src=='"') ! 77: *(dst++)='"'; /* escape quotes */ ! 78: *(dst++)=*src++; ! 79: } ! 80: if(enclose) ! 81: *(dst++)='"'; ! 82: ! 83: *dst=0; ! 84: ! 85: return(buf); ! 86: } ! 87: ! 88: char* csvLineCreator(const str_list_t columns) ! 89: { ! 90: char* str=NULL; ! 91: char* p; ! 92: char* val; ! 93: size_t i,len; ! 94: ! 95: if(columns==NULL) ! 96: return(NULL); ! 97: ! 98: for(i=0;columns[i]!=NULL;i++) { ! 99: len=strlen(columns[i])*2; ! 100: if(str) ! 101: len+=strlen(str); ! 102: if((p=realloc(str,len))==NULL) ! 103: break; ! 104: str=p; ! 105: if(i) strcat(str,","); ! 106: else *str=0; ! 107: if((val=csvEncode(columns[i]))==NULL) ! 108: break; ! 109: strcat(str,val); ! 110: free(val); ! 111: } ! 112: ! 113: return(str); ! 114: } ! 115: ! 116: str_list_t csvLineParser(const char* line) ! 117: { ! 118: char* p; ! 119: char* buf; ! 120: char* tmp; ! 121: size_t count=0; ! 122: str_list_t list; ! 123: ! 124: if((list=strListInit())==NULL) ! 125: return(NULL); ! 126: ! 127: if((buf=strdup(line))==NULL) { ! 128: strListFree(&list); ! 129: return(NULL); ! 130: } ! 131: ! 132: truncsp(buf); ! 133: ! 134: for(p=strtok_r(buf,",",&tmp);p;p=strtok_r(NULL,",",&tmp)) ! 135: strListAppend(&list,p,count++); ! 136: ! 137: free(buf); ! 138: ! 139: return(list); ! 140: } ! 141: ! 142: /*********************/ ! 143: /* Tab-Delimited API */ ! 144: /*********************/ ! 145: ! 146: char* tabLineCreator(const str_list_t columns) ! 147: { ! 148: char* str=NULL; ! 149: char* p; ! 150: size_t i,len; ! 151: ! 152: if(columns==NULL) ! 153: return(NULL); ! 154: ! 155: for(i=0;columns[i]!=NULL;i++) { ! 156: len=strlen(columns[i])*2; ! 157: if(str) ! 158: len+=strlen(str); ! 159: if((p=realloc(str,len))==NULL) ! 160: break; ! 161: str=p; ! 162: if(i) strcat(str,"\t"); ! 163: else *str=0; ! 164: strcat(str,columns[i]); ! 165: } ! 166: ! 167: return(str); ! 168: } ! 169: ! 170: str_list_t tabLineParser(const char* line) ! 171: { ! 172: char* p; ! 173: char* buf; ! 174: char* tmp; ! 175: size_t count=0; ! 176: str_list_t list; ! 177: ! 178: if((list=strListInit())==NULL) ! 179: return(NULL); ! 180: ! 181: if((buf=strdup(line))==NULL) { ! 182: strListFree(&list); ! 183: return(NULL); ! 184: } ! 185: ! 186: for(p=strtok_r(buf,"\t",&tmp);p;p=strtok_r(NULL,"\t",&tmp)) ! 187: strListAppend(&list,p,count++); ! 188: ! 189: free(buf); ! 190: ! 191: return(list); ! 192: } ! 193: ! 194: /* Generic API */ ! 195: ! 196: str_list_t dataCreateList(const str_list_t records[], const str_list_t columns, dataLineCreator_t lineCreator) ! 197: { ! 198: char* p; ! 199: str_list_t list; ! 200: size_t i; ! 201: size_t li=0; ! 202: ! 203: if((list=strListInit())==NULL) ! 204: return(NULL); ! 205: ! 206: if(columns!=NULL) { ! 207: p=lineCreator(columns); ! 208: strListAppend(&list,p,li++); ! 209: free(p); ! 210: } ! 211: ! 212: if(records!=NULL) ! 213: for(i=0;records[i]!=NULL;i++) { ! 214: p=lineCreator(records[i]); ! 215: strListAppend(&list,p,li++); ! 216: free(p); ! 217: } ! 218: ! 219: return(list); ! 220: } ! 221: ! 222: BOOL dataWriteFile(FILE* fp, const str_list_t records[], const str_list_t columns, const char* separator ! 223: ,dataLineCreator_t lineCreator) ! 224: { ! 225: size_t count,total; ! 226: str_list_t list; ! 227: ! 228: rewind(fp); ! 229: ! 230: if(chsize(fileno(fp),0)!=0) /* truncate */ ! 231: return(FALSE); ! 232: ! 233: if((list=dataCreateList(records,columns,lineCreator))==NULL) ! 234: return(FALSE); ! 235: ! 236: total = strListCount(list); ! 237: count = strListWriteFile(fp,list,separator); ! 238: strListFree(&list); ! 239: ! 240: return(count == total); ! 241: } ! 242: ! 243: str_list_t* dataParseList(const str_list_t records, str_list_t* columns, dataLineParser_t lineParser) ! 244: { ! 245: size_t ri=0; ! 246: size_t li=0; ! 247: str_list_t* list; ! 248: ! 249: if(records==NULL) ! 250: return(NULL); ! 251: ! 252: if((list=(str_list_t*)malloc(sizeof(str_list_t*)*(strListCount(records)+1)))==NULL) ! 253: return(NULL); ! 254: ! 255: if(columns!=NULL) { ! 256: if((*columns=lineParser(records[ri++]))==NULL) ! 257: return(NULL); ! 258: } ! 259: ! 260: while(records[ri]!=NULL) ! 261: list[li++]=lineParser(records[ri++]); ! 262: ! 263: list[li]=NULL; /* terminate */ ! 264: ! 265: return(list); ! 266: } ! 267: ! 268: str_list_t* dataReadFile(FILE* fp, str_list_t* columns, dataLineParser_t lineParser) ! 269: { ! 270: str_list_t* records; ! 271: str_list_t lines; ! 272: size_t i; ! 273: ! 274: rewind(fp); ! 275: ! 276: if((lines=strListReadFile(fp, NULL, 0))==NULL) ! 277: return(NULL); ! 278: ! 279: /* truncate line-feed chars off end of strings */ ! 280: for(i=0; lines[i]!=NULL; i++) ! 281: truncnl(lines[i]); ! 282: ! 283: records=dataParseList(lines,columns,lineParser); ! 284: ! 285: strListFree(&lines); ! 286: ! 287: return(records); ! 288: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.