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