|
|
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.