1 |
ben |
1 |
/* |
2 |
|
|
* csv2latex.c, copyright © 2002- Benoît Rouits <brouits@free.fr> |
3 |
|
|
* |
4 |
|
|
********************************************************* |
5 |
|
|
* csv2latex translates a .csv file to a LaTex document. * |
6 |
|
|
********************************************************* |
7 |
|
|
* |
8 |
|
|
* This program is free software; you can redistribute it and/or |
9 |
|
|
* modify it under the terms of the GNU General Public License |
10 |
|
|
* as published by the Free Software Foundation; version 2 only |
11 |
|
|
* of the License. |
12 |
|
|
* |
13 |
|
|
* This program is distributed in the hope that it will be useful, |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
|
|
* GNU General Public License for more details. |
17 |
|
|
* |
18 |
|
|
* You should have received a copy of the GNU General Public License |
19 |
|
|
* along with this program; if not, write to the Free Software |
20 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 |
|
|
* Boston, MA 02110-1301, USA. |
22 |
|
|
* |
23 |
|
|
* see the COPYING file included in the csv2latex package or |
24 |
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt |
25 |
|
|
* |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
#include <stdlib.h> |
29 |
|
|
#include <stdio.h> |
30 |
|
|
#include <string.h> |
31 |
|
|
#include <libgen.h> |
32 |
|
|
#include <getopt.h> |
33 |
|
|
#include <unistd.h> |
34 |
|
|
#include <ctype.h> |
35 |
|
|
#include "version.h" |
36 |
|
|
|
37 |
|
|
typedef struct { |
38 |
|
|
char* tab; /* actual escapes */ |
39 |
|
|
int size; /* escape tab len */ |
40 |
|
|
} texcape; |
41 |
|
|
|
42 |
|
|
typedef struct { |
43 |
|
|
char block; /* CSV delimitor if any */ |
44 |
|
|
char sep; /* CSV separator */ |
45 |
|
|
unsigned int cols; /* CSV columns */ |
46 |
|
|
unsigned int chars; /* CSV max data length */ |
47 |
|
|
unsigned int rows; /* CSV total number of lines */ |
48 |
|
|
char pos; /* position in cell (align) */ |
49 |
|
|
unsigned int lines; /* rows per LaTeX tabular */ |
50 |
|
|
unsigned int guess; /* guess or not the CSV format */ |
51 |
|
|
unsigned int header; /* put LaTeX document header or not */ |
52 |
|
|
unsigned int red; /* table reduction level (from 1 to 4)*/ |
53 |
|
|
unsigned int longtable; /* use package longtable */ |
54 |
|
|
unsigned int escape; /* escape TeX control chars or not */ |
55 |
ben |
6 |
unsigned int repeat; /* repeat table headers for each LaTeX table section or not */ |
56 |
|
|
unsigned int vlines; /* insert vertical lines between columns or not */ |
57 |
|
|
unsigned int hlines; /* insert horizontal lines between rows or not */ |
58 |
ben |
1 |
char* clrrow; /* row graylevel (from 0 to 1) */ |
59 |
|
|
texcape* tex; /* TeX escapes */ |
60 |
|
|
} config; |
61 |
|
|
|
62 |
|
|
#define MAXUINT ((unsigned int)(-1)) |
63 |
|
|
|
64 |
|
|
void rtfm(char* prog) { |
65 |
|
|
printf("%s translates a csv file to a LaTeX file\n", basename(prog)); |
66 |
|
|
printf("Example: %s january_stats.csv > january_stats.tex\n", basename(prog)); |
67 |
|
|
printf("Usage: %s [--nohead] (LaTeX) no document header: useful for inclusion\n", basename(prog)); |
68 |
|
|
printf(" [--longtable] (LaTeX) use package longtable: useful for long input\n"); |
69 |
|
|
printf(" [--noescape] (LaTeX) do not escape text: useful for mixed CSV/TeX input\n"); |
70 |
|
|
printf(" [--guess] (CSV) guess separator and block |\n" |
71 |
|
|
" [--separator <(c)omma|(s)emicolon|(t)ab|s(p)ace|co(l)on>] (CSV's comma)\n" |
72 |
|
|
" [--block <(q)uote|(d)ouble|(n)one>] (CSV) block delimiter (e.g: none)\n"); |
73 |
|
|
printf(" [--lines n] (LaTeX) rows per table: useful for long tabulars\n"); |
74 |
|
|
printf(" [--position <l|c|r>] (LaTeX) text align in cells\n"); |
75 |
|
|
printf(" [--colorrows graylevel] (LaTeX) alternate gray rows (e.g: 0.75)\n"); |
76 |
|
|
printf(" [--reduce level] (LaTeX) reduce table size (e.g: 1)\n"); |
77 |
ben |
6 |
printf(" [--repeatheader] (LaTeX) repeat table header (for long tables)\n"); |
78 |
|
|
printf(" [--nohlines] (LaTeX) don't put hline between table rows\n"); |
79 |
|
|
printf(" [--novlines] (LaTeX) don't put vline between columns\n"); |
80 |
ben |
1 |
printf(" csv_file.csv\n"); |
81 |
|
|
printf("The \"longtable\" option needs the {longtable} LaTeX package\n"); |
82 |
|
|
printf("The \"colorrows\" option needs the {colortbl} LaTeX package\n"); |
83 |
|
|
printf("The \"reduce\" option needs the {relsize} LaTeX package\n"); |
84 |
|
|
return; |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
config* parseOptions (config* conf, int argc, char **argv) { |
88 |
|
|
/* thx to <vfebvre@lautre.net> */ |
89 |
|
|
int opt; |
90 |
|
|
int tmp; |
91 |
|
|
|
92 |
|
|
#if defined USE_GETOPT |
93 |
|
|
#else |
94 |
|
|
int longopt_index = 0; |
95 |
|
|
static struct option long_options[] = { |
96 |
ben |
6 |
{"help", 0, NULL, 'h'}, |
97 |
|
|
{"guess", 0, NULL, 'g'}, |
98 |
|
|
{"block", 1, NULL, 'b'}, |
99 |
|
|
{"lines", 1, NULL, 'l'}, |
100 |
|
|
{"noescape", 0, NULL, 'x'}, |
101 |
|
|
{"nohead", 0, NULL, 'n'}, |
102 |
|
|
{"version", 0, NULL, 'v'}, |
103 |
|
|
{"position", 1, NULL, 'p'}, |
104 |
|
|
{"separator", 1, NULL, 's'}, |
105 |
|
|
{"colorrows", 1, NULL, 'c'}, |
106 |
|
|
{"reduce", 1, NULL, 'r'}, |
107 |
|
|
{"longtable", 0, NULL, 't'}, |
108 |
|
|
{"repeatheader",0, NULL, 'e'}, |
109 |
|
|
{"novlines", 0, NULL, 'y'}, |
110 |
|
|
{"nohlines", 0, NULL, 'z'}, |
111 |
ben |
1 |
{NULL, 0, NULL, 0} /* marks end-of-list */ |
112 |
|
|
}; |
113 |
|
|
#endif |
114 |
|
|
#if defined USE_GETOPT |
115 |
ben |
6 |
while ((opt = getopt (argc, argv, "hvgnxteyz?b:l:p:s:c:r:")) != EOF) { |
116 |
ben |
1 |
#else |
117 |
ben |
6 |
while ((opt = getopt_long (argc, argv, "hvgnxteyz?b:l:p:s:c:r:", long_options, &longopt_index)) > 0) { |
118 |
ben |
1 |
#endif |
119 |
|
|
switch (opt) { |
120 |
|
|
case '?': |
121 |
|
|
case 'h': |
122 |
|
|
rtfm (argv[0]); |
123 |
|
|
exit (EXIT_SUCCESS); |
124 |
|
|
break; |
125 |
|
|
case 'g': /* guess the CSV */ |
126 |
|
|
conf->guess=1; |
127 |
|
|
break; |
128 |
|
|
case 't': /* use package longtable */ /* thanks to <Christof.Bodner@infineon.com> */ |
129 |
|
|
conf->longtable=1; |
130 |
|
|
break; |
131 |
|
|
case 'b': /* csv block delimiter */ |
132 |
|
|
if(optarg[0] == 'q') |
133 |
|
|
conf->block = '\''; |
134 |
|
|
else if(optarg[0] == 'd') |
135 |
|
|
conf->block = '"'; |
136 |
|
|
else if(optarg[0] == 'n') |
137 |
|
|
conf->block = 0; /* no block delimiter */ |
138 |
|
|
break; |
139 |
|
|
case 'l': /* number of lines per TeX tabulars */ |
140 |
|
|
if(isdigit(optarg[0])) { |
141 |
|
|
conf->lines=atoi(optarg); |
142 |
|
|
} else { |
143 |
|
|
fprintf(stderr, |
144 |
|
|
"option \"lines\" need a positive integer value\n"); |
145 |
|
|
exit(EXIT_FAILURE); |
146 |
|
|
} |
147 |
|
|
break; |
148 |
|
|
case 'n': |
149 |
|
|
conf->header=0; |
150 |
|
|
break; |
151 |
|
|
case 'x': |
152 |
|
|
conf->escape=0; |
153 |
|
|
break; |
154 |
|
|
case 'v': /* version */ |
155 |
|
|
printf ("%s © 2002- Benoît Rouits <brouits@free.fr>\n" |
156 |
|
|
"\tVersion %s (%s)\n", PACKAGE, VERSION, RELEASE_DATE); |
157 |
|
|
exit (EXIT_SUCCESS); |
158 |
|
|
break; |
159 |
|
|
case 'p': /* LaTeX position in cell */ |
160 |
|
|
conf->pos=optarg[0]; /* position char in cell */ |
161 |
|
|
break; |
162 |
|
|
case 's': /* csv block separator */ |
163 |
|
|
if(optarg[0] == 'c') |
164 |
|
|
conf->sep = ','; |
165 |
|
|
else if(optarg[0] == 's') |
166 |
|
|
conf->sep = ';'; |
167 |
|
|
else if(optarg[0] == 't') |
168 |
|
|
conf->sep = '\t'; |
169 |
|
|
else if(optarg[0] == 'p') |
170 |
|
|
conf->sep = ' '; |
171 |
|
|
else if(optarg[0] == 'l') |
172 |
|
|
conf->sep = ':'; |
173 |
|
|
break; |
174 |
|
|
case 'c': /* color rows (thx to <jcorso@cse.Buffalo.EDU>) */ |
175 |
|
|
if(isdigit(optarg[0])) { |
176 |
|
|
conf->clrrow = (char*)malloc(strlen(optarg)+1); |
177 |
|
|
strcpy(conf->clrrow,optarg); |
178 |
|
|
} else { |
179 |
|
|
fprintf(stderr, |
180 |
|
|
"option \"colorrows\" needs a real value between 0 and 1\n"); |
181 |
|
|
exit(EXIT_FAILURE); |
182 |
|
|
} |
183 |
|
|
break; |
184 |
|
|
case 'r': /* reduce table size (original idea thx to <boaz.gezer@gmail.com>) */ |
185 |
|
|
|
186 |
|
|
if(isdigit(optarg[0])) { |
187 |
|
|
tmp = atoi(optarg); |
188 |
|
|
conf->red=(tmp>4)?4:(tmp<0)?0:tmp; /* [1-4] */ |
189 |
|
|
} else { |
190 |
|
|
fprintf(stderr, |
191 |
|
|
"option \"reduce\" needs an integer value between 1 and 4\n"); |
192 |
|
|
exit(EXIT_FAILURE); |
193 |
|
|
} |
194 |
|
|
break; |
195 |
ben |
6 |
case 'e': /*repeat table header for each table section*/ |
196 |
|
|
conf->repeat=1; |
197 |
|
|
break; |
198 |
|
|
case 'y': /*don't draw vlines between columns*/ |
199 |
|
|
conf->vlines=0; |
200 |
|
|
break; |
201 |
|
|
case 'z': /*don't draw hlines between rows*/ |
202 |
|
|
conf->hlines=0; |
203 |
|
|
break; |
204 |
ben |
1 |
} |
205 |
|
|
} |
206 |
|
|
return conf; |
207 |
|
|
} |
208 |
|
|
int guessCSV(config* conf, FILE* in) { |
209 |
|
|
/* guess the block delimiter and the csv separator */ |
210 |
|
|
int token; |
211 |
|
|
|
212 |
|
|
token=getc(in); /* first char is block delimiter */ |
213 |
|
|
if(token == EOF) { |
214 |
|
|
fprintf(stderr,"ERROR: emtpy file ?\n"); |
215 |
|
|
return(-1); |
216 |
|
|
} else if (ispunct(token) || token == ' ') { |
217 |
|
|
/* found first block delimiter, act this way */ |
218 |
|
|
conf->block=token; |
219 |
|
|
fprintf(stderr,"Guessed '%c' as Block Delimiter\n", |
220 |
|
|
conf->block); |
221 |
|
|
/* stream file while token is printable data */ |
222 |
|
|
while((token=getc(in)) != conf->block && |
223 |
|
|
token != '\n' && |
224 |
|
|
token != EOF) |
225 |
|
|
{/* getc has been done */} |
226 |
|
|
if(token == conf->block){ |
227 |
|
|
/* second delimiter : next is separator */ |
228 |
|
|
conf->sep=getc(in); |
229 |
|
|
fprintf(stderr,"Guessed '%c' as Separator\n", |
230 |
|
|
conf->sep); |
231 |
|
|
return(0); |
232 |
|
|
}else{ |
233 |
|
|
return (-1); /* what else ? */ |
234 |
|
|
} |
235 |
|
|
}else{ /* no block delimiter, act this way */ |
236 |
|
|
conf->block=0; |
237 |
|
|
fprintf(stderr,"Guessed No Block Delimiter\n"); |
238 |
|
|
/* stream file while input is not a control char */ |
239 |
|
|
while(isalnum((token=getc(in))) && |
240 |
|
|
token != '\n' && |
241 |
|
|
token != EOF) |
242 |
|
|
{/* getc has been done */} |
243 |
|
|
/* guess CSV separator */ |
244 |
|
|
if(ispunct(token) || token == '\t' || token == ' '){ |
245 |
|
|
conf->sep=token; |
246 |
|
|
fprintf(stderr,"Guessed %c as Separator\n", conf->sep); |
247 |
|
|
return(0); |
248 |
|
|
} else { /* did not found any separator */ |
249 |
|
|
fprintf(stderr,"ERROR: Did not guess any Separator!\n"); |
250 |
|
|
return(-1); |
251 |
|
|
} |
252 |
|
|
} |
253 |
|
|
return(0); |
254 |
|
|
} |
255 |
|
|
|
256 |
|
|
void getMaximums(config* conf, FILE* in) { |
257 |
|
|
/* gets the number of cols and chars of a csv file assuming a separator */ |
258 |
|
|
int token=0; |
259 |
|
|
unsigned int curcol=0; |
260 |
|
|
unsigned int curchar=0; |
261 |
|
|
unsigned int inblock=0; |
262 |
|
|
/* init */ |
263 |
|
|
conf->chars=0; |
264 |
|
|
conf->cols=0; |
265 |
|
|
conf->rows=0; |
266 |
|
|
|
267 |
|
|
while (token != EOF) { |
268 |
|
|
token=getc(in); |
269 |
|
|
|
270 |
|
|
/* EOF ? */ |
271 |
|
|
if (token == EOF) { |
272 |
|
|
continue; |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
/* decide the maximums */ |
276 |
|
|
if (token == '\n') { |
277 |
|
|
curcol++; |
278 |
|
|
conf->cols=(conf->cols<curcol)?curcol:conf->cols; |
279 |
|
|
conf->chars=(conf->chars<curchar)?curchar:conf->chars; |
280 |
|
|
conf->rows++; |
281 |
|
|
curcol=0; |
282 |
|
|
curchar=0; |
283 |
|
|
inblock=0; /* reset block state */ |
284 |
|
|
continue; |
285 |
|
|
} |
286 |
|
|
|
287 |
|
|
/* enter/quit a block */ |
288 |
|
|
if (conf->block && token == conf->block) { |
289 |
|
|
inblock=!inblock; |
290 |
|
|
continue; |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
/* count cols in current line */ |
294 |
|
|
if (token == conf->sep && ((conf->block && !inblock) || !conf->block)) { |
295 |
|
|
curcol++; |
296 |
|
|
continue; |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
/* count chars in current cell */ |
300 |
|
|
if (token != conf->block && ((conf->block && inblock) || !conf->block)) { |
301 |
|
|
curchar++; |
302 |
|
|
continue; |
303 |
|
|
} |
304 |
|
|
} |
305 |
|
|
return; |
306 |
|
|
} |
307 |
|
|
|
308 |
|
|
void doTeXsub(config* conf, char newsep, FILE* in, FILE* out) { |
309 |
|
|
/* substitutes CSV sep by LaTeX newsep and some TeX code */ |
310 |
|
|
int token=0; |
311 |
|
|
int max; |
312 |
|
|
int numcols; |
313 |
ben |
6 |
unsigned int lines; |
314 |
ben |
1 |
int inblock=0; |
315 |
|
|
int csvrows; |
316 |
ben |
6 |
int firstrow=1; |
317 |
|
|
char headerrow[1000]; |
318 |
|
|
headerrow[0]='\0'; |
319 |
ben |
1 |
|
320 |
|
|
max=numcols=conf->cols; |
321 |
|
|
csvrows=conf->rows; |
322 |
|
|
/* choose infinity when conf->lines is 0 */ |
323 |
|
|
lines=(conf->lines)?conf->lines:MAXUINT; |
324 |
|
|
|
325 |
|
|
while (token!=EOF) { |
326 |
|
|
token=getc(in); |
327 |
|
|
|
328 |
|
|
/* EOF ? */ |
329 |
|
|
if (token == EOF) { |
330 |
|
|
continue; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
/* new line ? */ |
334 |
|
|
if (token == '\n') { |
335 |
|
|
inblock = 0; /* close block if any */ |
336 |
|
|
/* fill empty cols if any */ |
337 |
|
|
while (numcols > 1) { |
338 |
|
|
putc(newsep,out); |
339 |
|
|
numcols--; |
340 |
|
|
} |
341 |
ben |
6 |
if (!(firstrow && (conf->longtable && conf->repeat))) { |
342 |
|
|
fprintf(out,"\\\\\n"); /* TeX new line */ |
343 |
|
|
} else { /* first row and repeat and longtable */ |
344 |
|
|
fprintf(out, "%s\\\\\n", headerrow); |
345 |
|
|
} |
346 |
|
|
if(conf->hlines) { |
347 |
|
|
fprintf(out,"\\hline\n"); /* TeX draw hline */ |
348 |
|
|
} |
349 |
|
|
if (firstrow && (conf->longtable && conf->repeat)) { |
350 |
|
|
fprintf(out, "%s", "\\endhead\n"); |
351 |
|
|
} |
352 |
|
|
if(firstrow && conf->repeat) { |
353 |
|
|
char tmp[12]; |
354 |
|
|
sprintf(tmp,(conf->hlines?"\\\\\n\\hline":"\\\\\n")); |
355 |
|
|
strcat(headerrow,tmp); |
356 |
|
|
} |
357 |
|
|
firstrow=0; |
358 |
ben |
1 |
numcols=max; /* reset numcols */ |
359 |
|
|
lines--; |
360 |
|
|
csvrows--; |
361 |
|
|
/* put a colored row or not (alternate) */ |
362 |
ben |
6 |
if (conf->clrrow && (lines % 2)) { |
363 |
ben |
1 |
fprintf(out,"\\colorrow "); |
364 |
|
|
} |
365 |
ben |
6 |
/* if the LaTeX tabular is full create a new one, except if no more row or this is a long table */ |
366 |
|
|
if (!lines && csvrows && !conf->longtable) { |
367 |
|
|
fprintf(out,"\\end{tabular}\n"); |
368 |
|
|
fprintf(out, "\\newline\n"); |
369 |
|
|
fprintf(out,"\\begin{tabular}{"); |
370 |
|
|
if(conf->vlines) { |
371 |
|
|
putc('|',out); |
372 |
|
|
} |
373 |
|
|
while(numcols--) { |
374 |
|
|
putc(conf->pos, out); |
375 |
|
|
if(conf->vlines) { |
376 |
|
|
putc('|',out); |
377 |
|
|
} |
378 |
|
|
} |
379 |
ben |
1 |
fprintf(out, "}\n"); |
380 |
ben |
6 |
if(conf->hlines) { |
381 |
|
|
fprintf(out, "\\hline\n"); |
382 |
|
|
} |
383 |
|
|
if(conf->repeat && !conf->longtable) { |
384 |
|
|
fprintf(out,"%s",headerrow); |
385 |
|
|
putc('\n',out); |
386 |
|
|
} |
387 |
ben |
1 |
numcols=max; |
388 |
|
|
lines=(conf->lines)?conf->lines:MAXUINT; |
389 |
ben |
6 |
} |
390 |
|
|
/* else end of CSV data */ |
391 |
ben |
1 |
continue; |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
/* new column ? */ |
395 |
|
|
if (token == conf->sep && ((conf->block && !inblock) || !conf->block)) { |
396 |
ben |
6 |
if (!(firstrow && (conf->longtable && conf->repeat))) |
397 |
|
|
putc(newsep,out); |
398 |
ben |
1 |
numcols--; |
399 |
ben |
6 |
if(firstrow && conf->repeat) |
400 |
|
|
{ |
401 |
|
|
char tmp[2]; |
402 |
|
|
tmp[0]=newsep; |
403 |
|
|
tmp[1]='\0'; |
404 |
|
|
strcat(headerrow,tmp); |
405 |
|
|
} |
406 |
ben |
1 |
continue; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
/* enter/quit a block ? */ |
410 |
|
|
if (conf->block && token == conf->block) { |
411 |
|
|
inblock=!inblock; |
412 |
|
|
continue; |
413 |
|
|
} |
414 |
|
|
|
415 |
|
|
/* data ? */ |
416 |
|
|
if (token != conf->block && ((conf->block && inblock) || !conf->block)) { |
417 |
|
|
/* look for special TeX char to escape */ |
418 |
|
|
/* FIXME: put all that into a subroutine */ |
419 |
|
|
int i=0; |
420 |
|
|
if (conf->escape) |
421 |
|
|
for (i=0;i < conf->tex->size; i++) { |
422 |
|
|
if (token == conf->tex->tab[i]) { |
423 |
|
|
switch (token) { |
424 |
|
|
case '\\': |
425 |
|
|
fprintf(out, "\\textbackslash{}"); |
426 |
ben |
6 |
if(firstrow && conf->repeat) |
427 |
|
|
{ |
428 |
|
|
char tmp[17]; |
429 |
|
|
sprintf(tmp,"\\textbackslash{}"); |
430 |
|
|
strcat(headerrow,tmp); |
431 |
|
|
} |
432 |
ben |
1 |
break; |
433 |
|
|
default: |
434 |
|
|
fprintf(out, "\\%c", token); |
435 |
ben |
6 |
if(firstrow && conf->repeat) |
436 |
|
|
{ |
437 |
|
|
char tmp[3]; |
438 |
|
|
tmp[0]='\\'; |
439 |
|
|
tmp[1]=token; |
440 |
|
|
tmp[2]='\0'; |
441 |
|
|
strcat(headerrow,tmp); |
442 |
|
|
} |
443 |
ben |
1 |
break; |
444 |
|
|
} |
445 |
|
|
break; /* there was some escaping */ |
446 |
|
|
} |
447 |
|
|
} |
448 |
|
|
/* or print raw char */ |
449 |
|
|
if ( (i >= conf->tex->size) || (!conf->escape) ) { |
450 |
ben |
6 |
if (!(firstrow && (conf->longtable && conf->repeat))) |
451 |
|
|
putc(token, out); /* do not print the header twice */ |
452 |
|
|
if(firstrow && conf->repeat) |
453 |
|
|
{ |
454 |
|
|
char tmp[2]; |
455 |
|
|
tmp[0]=token; |
456 |
|
|
tmp[1]='\0'; |
457 |
|
|
strcat(headerrow,tmp); |
458 |
|
|
} |
459 |
ben |
1 |
} |
460 |
|
|
continue; |
461 |
|
|
} |
462 |
|
|
/* do nothing if unexpected char: just loop */ |
463 |
|
|
} |
464 |
|
|
return; |
465 |
|
|
} |
466 |
|
|
|
467 |
|
|
void doTeXdoc(config* conf, FILE* in, FILE* out) { |
468 |
|
|
/* prepares the LaTeX tabular layout */ |
469 |
|
|
int maxcols; |
470 |
|
|
int numcols; |
471 |
|
|
char* relsize[5] = {"0", "0.5", "1", "2", "4"}; /* LaTeX relsize good values */ |
472 |
|
|
char* tabcolsep[5] = {"0", "0.05", "0.1", "0.2", "0.4"}; /* LaTeX tabcolsep good values */ |
473 |
|
|
|
474 |
|
|
numcols=maxcols=conf->cols; |
475 |
|
|
if(conf->header){ |
476 |
|
|
fprintf(out, "\\documentclass[a4paper]{article}\n"); |
477 |
|
|
fprintf(out, "\\usepackage[T1]{fontenc}\n"); |
478 |
|
|
fprintf(out, "\\usepackage[latin1]{inputenc}\n"); |
479 |
|
|
if (conf->red){ |
480 |
|
|
fprintf(out,"\\usepackage{relsize}\n"); |
481 |
|
|
} |
482 |
|
|
if (conf->clrrow){ |
483 |
|
|
fprintf(out,"\\usepackage{colortbl}\n"); |
484 |
|
|
} |
485 |
|
|
if (conf->longtable){ |
486 |
|
|
fprintf(out,"\\usepackage{longtable}\n"); |
487 |
|
|
} |
488 |
|
|
fprintf(out, "\\begin{document}\n"); |
489 |
|
|
} |
490 |
|
|
if (conf->clrrow){ |
491 |
|
|
fprintf(out,"\\def\\colorrow{\\rowcolor[gray]{%s}}\n", |
492 |
|
|
conf->clrrow); |
493 |
|
|
} |
494 |
|
|
if (conf->red){ |
495 |
|
|
fprintf(out,"\\relsize{-%s}\n", relsize[conf->red]); |
496 |
|
|
fprintf(out,"\\addtolength\\tabcolsep{-%sem}\n", tabcolsep[conf->red]); |
497 |
|
|
} |
498 |
|
|
if (conf->longtable) |
499 |
ben |
6 |
{ |
500 |
|
|
fprintf(out, "\\begin{longtable}{"); |
501 |
|
|
if(conf->vlines) |
502 |
|
|
putc('|',out); |
503 |
|
|
} |
504 |
ben |
1 |
else |
505 |
ben |
6 |
{ |
506 |
|
|
fprintf(out, "\\begin{tabular}{"); |
507 |
|
|
if(conf->vlines) |
508 |
|
|
putc('|',out); |
509 |
|
|
} |
510 |
ben |
1 |
while(numcols--) |
511 |
ben |
6 |
{ |
512 |
|
|
fprintf(out, "%c",conf->pos); /* position in cell */ |
513 |
|
|
if(conf->vlines) |
514 |
|
|
putc('|',out); |
515 |
|
|
} |
516 |
ben |
1 |
fprintf(out, "}\n"); |
517 |
ben |
6 |
if(conf->hlines) |
518 |
|
|
fprintf(out, "\\hline\n"); |
519 |
ben |
1 |
doTeXsub(conf, '&', in, out); /* & is LaTeX separator */ |
520 |
|
|
if (conf->longtable) { |
521 |
|
|
fprintf(out, "\\end{longtable}\n"); |
522 |
|
|
} else { |
523 |
|
|
fprintf(out, "\\end{tabular}\n"); |
524 |
|
|
} |
525 |
|
|
if (conf->red){ |
526 |
|
|
fprintf(out,"\\addtolength\\tabcolsep{+%sem}\n", tabcolsep[conf->red]); |
527 |
|
|
fprintf(out,"\\relsize{+%s}\n", relsize[conf->red]); |
528 |
|
|
} |
529 |
|
|
if(conf->header){ |
530 |
|
|
fprintf(out, "\\end{document}\n"); |
531 |
|
|
} |
532 |
|
|
return; |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
int main (int argc, char **argv) { |
536 |
|
|
FILE* fp; |
537 |
|
|
config* conf; |
538 |
|
|
|
539 |
|
|
extern int optind, opterr, optopt; |
540 |
|
|
|
541 |
|
|
if(argc == 1){ |
542 |
|
|
rtfm(argv[0]); |
543 |
|
|
exit(EXIT_SUCCESS); |
544 |
|
|
} |
545 |
|
|
conf=(config*)malloc(sizeof(config)); |
546 |
|
|
/* defaults (ensure init): */ |
547 |
|
|
conf->cols=1; /* CSV: if getMaximums fails */ |
548 |
|
|
conf->rows=0; /* CSV: must be 0 */ |
549 |
|
|
conf->chars=0; /* CSV: must be 0 */ |
550 |
|
|
conf->pos='l'; /* usual; LaTeX */ |
551 |
|
|
conf->lines=40; /* usual; LaTeX */ |
552 |
|
|
conf->guess=0; /* usual */ |
553 |
|
|
conf->sep=','; /* default; csv */ |
554 |
|
|
conf->block=0; /* default; csv */ |
555 |
|
|
conf->header=1; /* usual; LaTeX */ |
556 |
|
|
conf->escape=1; /* usual; LaTeX */ |
557 |
|
|
conf->clrrow=NULL; /* default; LaTeX */ |
558 |
|
|
conf->red=0; /* default; LaTeX */ |
559 |
|
|
conf->longtable=0; /* default; without package longtable */ |
560 |
ben |
6 |
conf->repeat=0; /* default; do not repeat the header row */ |
561 |
|
|
conf->vlines=1; /* default; draw lines between columns */ |
562 |
|
|
conf->hlines=1; /* default; draw lines between rows */ |
563 |
ben |
1 |
|
564 |
|
|
/* TeX charaters to escape */ |
565 |
|
|
conf->tex=(texcape*)malloc(sizeof(texcape)); |
566 |
|
|
conf->tex->tab = "\\_#$%^&{}~"; |
567 |
|
|
conf->tex->size = 10; |
568 |
|
|
|
569 |
|
|
conf=parseOptions(conf, argc, argv); |
570 |
|
|
fp=fopen(argv[optind],"r"); |
571 |
|
|
if (!fp){ |
572 |
|
|
fprintf(stderr,"Can't open file %s\n", argv[optind]); |
573 |
|
|
exit(EXIT_FAILURE); |
574 |
|
|
} |
575 |
|
|
if(conf->guess){ |
576 |
|
|
if(guessCSV(conf, fp)){ |
577 |
|
|
fprintf(stderr,"Please run again by using -- delimiter (if any) and --separator\n"); |
578 |
|
|
exit(EXIT_FAILURE); |
579 |
|
|
} |
580 |
|
|
rewind(fp); |
581 |
|
|
} |
582 |
|
|
getMaximums(conf, fp); |
583 |
|
|
rewind(fp); |
584 |
|
|
doTeXdoc(conf, fp, stdout); |
585 |
|
|
free(conf->tex); |
586 |
|
|
if (conf->clrrow) free(conf->clrrow); conf->clrrow=NULL; |
587 |
|
|
free(conf); |
588 |
|
|
fclose(fp); |
589 |
|
|
return 0; |
590 |
|
|
} |