1 /****************************************************************************
2  * csv.c
3  ****************************************************************************
4  * dbf Reader and Converter for dBASE files
5  * Implementation
6  *
7  * Author: Bjoern Berg <clergyman@gmx.de>
8  * Modifications: Uwe Steinmann <uwe@steinmann.cx>
9  *
10  ****************************************************************************
11  * Functions to write CSV files
12  ****************************************************************************
13  * $Id: csv.c,v 1.21 2006/04/10 15:07:21 steinm Exp $
14  ***************************************************************************/
15 
16 #include <libdbf/libdbf.h>
17 #include "csv.h"
18 
19 /* CSVFileType describes the type the converted file is of: (C)SV or (T)SV */
20 static char CSVFileType = 'C';
21 static char CSVSeparator = ',';
22 static char CSVEnclosure = '"';
23 static int CSVTableStructure = 1;
24 
25 /* setCSVSep() {{{
26  * allows to change the separator used for CSV output
27  */
28 int
setCSVSep(FILE * fp,P_DBF * p_dbf,const char * in,const char * separator)29 setCSVSep(FILE *fp, P_DBF *p_dbf,
30     const char *in /* __unused */, const char *separator)
31 {
32 	if ( separator[1] && separator[0] != 't' ) {
33 		fprintf(stderr, _("Separator / Escape char ``%s'' is too long -- must be a single character."),
34 		    separator);
35 		fprintf(stderr, "\n");
36 		return 1;
37 	} else if ( separator[0] == 't' ) {
38 		CSVSeparator = '\t';
39 		CSVFileType = 'T';
40 	} else {
41 		CSVSeparator = separator[0];
42 	}
43 	return 0;
44 }
45 /* }}} */
46 
47 /* writeCSVHeader() {{{
48  * creates the CSV Header with the information provided by DB_FIELD
49  */
50 int
writeCSVHeader(FILE * fp,P_DBF * p_dbf,const char * in,const char * out)51 writeCSVHeader (FILE *fp, P_DBF *p_dbf,
52     const char *in /* __unused */, const char *out /* __unused */)
53 {
54 	int i, columns;
55 
56 	columns = dbf_NumCols(p_dbf);
57 	for (i = 0; i < columns; i++) {
58 		char field_type;
59 		const char *field_name;
60 		int field_length, field_decimals;
61 		field_type = dbf_ColumnType(p_dbf, i);
62 		field_name = dbf_ColumnName(p_dbf, i);
63 		field_length = dbf_ColumnSize(p_dbf, i);
64 		field_decimals = dbf_ColumnDecimals(p_dbf, i);
65 		if(CSVTableStructure && CSVSeparator == ',')
66 			fputs("\"", fp);
67 		fprintf(fp, "%s", field_name);
68 		if(CSVTableStructure) {
69 			fprintf(fp, ",%c", field_type);
70 			switch(field_type) {
71 				case 'C':
72 					fprintf(fp, ",%d", field_length);
73 					break;
74 				case 'N':
75 					fprintf(fp, ",%d,%d", field_length, field_decimals);
76 					break;
77 			}
78 		}
79 		if(CSVTableStructure && CSVSeparator == ',')
80 			fputs("\"", fp);
81 		if(i < columns-1)
82 			putc(CSVSeparator, fp);
83 	}
84 	fputs("\n", fp);
85 
86 	return 0;
87 }
88 /* }}} */
89 
90 /* writeCSVLine {{{
91  * creates a line in the CSV document for each data set
92  */
93 int
writeCSVLine(FILE * fp,P_DBF * p_dbf,const unsigned char * value,int record_length,const char * in,const char * out)94 writeCSVLine(FILE *fp, P_DBF *p_dbf,
95     const unsigned char *value, int record_length,
96     const char *in /* unused */, const char *out /* unused */)
97 {
98 	int i, columns;
99 	int needsencl;
100 	const char *ptr;
101 
102 	columns = dbf_NumCols(p_dbf);
103 
104 	for (i = 0; i < columns; i++) {
105 		const unsigned char *end, *begin;
106 		int isstring, isfloat;
107 		char field_type;
108 		const char *field_name;
109 		int field_length, field_decimals;
110 		int dbversion = dbf_GetVersion(p_dbf);
111 		field_type = dbf_ColumnType(p_dbf, i);
112 		field_name = dbf_ColumnName(p_dbf, i);
113 		field_length = dbf_ColumnSize(p_dbf, i);
114 		field_decimals = dbf_ColumnDecimals(p_dbf, i);
115 
116 		isstring = field_type == 'M' || field_type == 'C';
117 		isfloat = field_type == 'F' || (field_type == 'B' && dbversion == VisualFoxPro) ? 1 : 0;
118 
119 		begin = value;
120 		value += field_length;
121 		end = value - 1;
122 
123 		/* Remove NULL chars at end of field */
124 		while(end != begin && *end == '\0')
125 			end--;
126 
127 		/* Check if enclosure chars are needed. This is the case if
128 		 * the value contains the char for separating the columns
129 		 */
130 		ptr = begin;
131 		needsencl = 0;
132 		while(!needsencl && ptr <= end) {
133 			if(*ptr == CSVSeparator || *ptr == CSVEnclosure)
134 				needsencl = 1;
135 			ptr++;
136 		}
137 
138 		/*
139 		 * addded to keep to CSV standard:
140 		 * Text fields must be enclosed by quotation marks
141 		 * - berg, 2003-09-08
142 		 */
143 		if ( needsencl )
144 			putc(CSVEnclosure, fp);
145 
146 		while (*begin == ' ' && begin != end)
147 			begin++;
148 
149 		if (*begin != ' ') {
150 
151 			while (*end == ' ')
152 				end--;
153 
154 			/* This routine must be verified in several tests */
155 			if (isfloat) {
156 				char *fmt = malloc(20);
157 				sprintf(fmt, "%%%d.%df", field_length, field_decimals);
158 				fprintf(fp, fmt, *(double *)begin);
159 				begin += field_length;
160 			} else {
161 				do {
162 					/* mask enclosure char */
163 					if(*begin == CSVEnclosure) {
164 							putc(CSVEnclosure, fp);
165 					}
166 					putc(*begin, fp);
167 				} while (begin++ < end);
168 			}
169 		}
170 
171 		if ( needsencl )
172 			putc(CSVEnclosure, fp);
173 
174 		if(i < columns-1)
175 			putc(CSVSeparator, fp);
176 	}
177 	fputs("\n", fp);
178 
179 	return 0;
180 }
181 /* }}} */
182 
183 /*
184  * Local variables:
185  * tab-width: 4
186  * c-basic-offset: 4
187  * End:
188  * vim600: sw=4 ts=4 fdm=marker
189  * vim<600: sw=4 ts=4
190  */
191