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 |
#define PTOMM(p) (0.3528 * (p)) /* pt to mm */ |
38 |
#define HEIGHT PTOMM(592) /* \textheight default in pt */ |
39 |
#define SKIPR 1.2 /* default \baselineskip ratio (to multiply with font) */ |
40 |
|
41 |
typedef struct { |
42 |
char* tab; /* actual escapes */ |
43 |
int size; /* escape tab len */ |
44 |
} texcape; |
45 |
|
46 |
typedef struct { |
47 |
char block; /* CSV delimitor if any */ |
48 |
char sep; /* CSV separator */ |
49 |
unsigned int cols; /* CSV columns */ |
50 |
unsigned int chars; /* CSV max data length */ |
51 |
unsigned int rows; /* CSV total number of lines */ |
52 |
char pos; /* position in cell (align) */ |
53 |
unsigned int lines; /* rows per LaTeX tabular */ |
54 |
unsigned int guess; /* guess or not the CSV format */ |
55 |
unsigned int header; /* put LaTeX document header or not */ |
56 |
unsigned int red; /* table reduction level (from 1 to 4)*/ |
57 |
unsigned int longtable; /* use package longtable */ |
58 |
unsigned int escape; /* escape TeX control chars or not */ |
59 |
unsigned int repeat; /* repeat table headers for each LaTeX table section or not */ |
60 |
unsigned int vlines; /* insert vertical lines between columns or not */ |
61 |
unsigned int hlines; /* insert horizontal lines between rows or not */ |
62 |
unsigned int paper; /* output paper type */ |
63 |
unsigned int font; /* expected font size */ |
64 |
unsigned int land; /* landscape mode */ |
65 |
char* clrrow; /* row graylevel (from 0 to 1) */ |
66 |
texcape* tex; /* TeX escapes */ |
67 |
} config; |
68 |
|
69 |
#define MAXUINT ((unsigned int)(-1)) |
70 |
|
71 |
void rtfm(char* prog) { |
72 |
printf("%s translates a csv file to a LaTeX file\n", basename(prog)); |
73 |
printf("Example: %s january_stats.csv > january_stats.tex\n", basename(prog)); |
74 |
printf("Usage: %s [--nohead] (LaTeX) no document header: useful for inclusion\n", basename(prog)); |
75 |
printf(" [--longtable] (LaTeX) use package longtable: useful for long input\n"); |
76 |
printf(" [--noescape] (LaTeX) do not escape text: useful for mixed CSV/TeX input\n"); |
77 |
printf(" [--guess] (CSV) guess separator and block |\n" |
78 |
" [--separator <(c)omma|(s)emicolon|(t)ab|s(p)ace|co(l)on>] (CSV's comma)\n" |
79 |
" [--block <(q)uote|(d)ouble|(n)one>] (CSV) block delimiter (e.g: none)\n"); |
80 |
printf(" [--lines n] (LaTeX) rows per table: useful for long tabulars |\n"); |
81 |
printf(" [--font n] font size used (in pt)\n"); |
82 |
printf(" [--position <l|c|r>] (LaTeX) text align in cells\n"); |
83 |
printf(" [--colorrows graylevel] (LaTeX) alternate gray rows (e.g: 0.75)\n"); |
84 |
printf(" [--reduce level] (LaTeX) reduce table size (e.g: 1)\n"); |
85 |
printf(" [--landscape] (LaTeX) use landscape mode\n"); |
86 |
printf(" [--repeatheader] (LaTeX) repeat table header (for long tables)\n"); |
87 |
printf(" [--nohlines] (LaTeX) don't put hline between table rows\n"); |
88 |
printf(" [--novlines] (LaTeX) don't put vline between columns\n"); |
89 |
printf(" csv_file.csv\n"); |
90 |
printf("The \"longtable\" option needs the {longtable} LaTeX package\n"); |
91 |
printf("The \"colorrows\" option needs the {colortbl} LaTeX package\n"); |
92 |
printf("The \"reduce\" option needs the {relsize} LaTeX package\n"); |
93 |
return; |
94 |
} |
95 |
|
96 |
config* parseOptions (config* conf, int argc, char **argv) { |
97 |
/* thx to <vfebvre@lautre.net> */ |
98 |
int opt; |
99 |
int tmp; |
100 |
|
101 |
#if defined USE_GETOPT |
102 |
#else |
103 |
int longopt_index = 0; |
104 |
static struct option long_options[] = { |
105 |
{"help", 0, NULL, 'h'}, |
106 |
{"guess", 0, NULL, 'g'}, |
107 |
{"block", 1, NULL, 'b'}, |
108 |
{"lines", 1, NULL, 'l'}, |
109 |
{"noescape", 0, NULL, 'x'}, |
110 |
{"nohead", 0, NULL, 'n'}, |
111 |
{"version", 0, NULL, 'v'}, |
112 |
{"position", 1, NULL, 'p'}, |
113 |
{"separator", 1, NULL, 's'}, |
114 |
{"colorrows", 1, NULL, 'c'}, |
115 |
{"reduce", 1, NULL, 'r'}, |
116 |
{"longtable", 0, NULL, 't'}, |
117 |
{"repeatheader", 0, NULL, 'e'}, |
118 |
{"novlines", 0, NULL, 'y'}, |
119 |
{"nohlines", 0, NULL, 'z'}, |
120 |
{"landscape", 0, NULL, 'a'}, |
121 |
{"font", 1, NULL, 'f'}, |
122 |
{NULL, 0, NULL, 0} /* marks end-of-list */ |
123 |
}; |
124 |
#endif |
125 |
#if defined USE_GETOPT |
126 |
while ((opt = getopt (argc, argv, "hvgnxteyza?b:l:p:s:c:r:f:")) != EOF) { |
127 |
#else |
128 |
while ((opt = getopt_long (argc, argv, "hvgnxteyza?b:l:p:s:c:r:f:", long_options, &longopt_index)) > 0) { |
129 |
#endif |
130 |
switch (opt) { |
131 |
case '?': |
132 |
case 'h': |
133 |
rtfm (argv[0]); |
134 |
exit (EXIT_SUCCESS); |
135 |
break; |
136 |
case 'g': /* guess the CSV */ |
137 |
conf->guess = 1; |
138 |
break; |
139 |
case 't': /* use package longtable */ /* thx to <Christof.Bodner@infineon.com> */ |
140 |
conf->longtable = 1; |
141 |
break; |
142 |
case 'b': /* csv block delimiter */ |
143 |
if (optarg[0] == 'q') |
144 |
conf->block = '\''; |
145 |
else if (optarg[0] == 'd') |
146 |
conf->block = '"'; |
147 |
else if (optarg[0] == 'n') |
148 |
conf->block = 0; /* no block delimiter */ |
149 |
break; |
150 |
case 'l': /* number of lines per TeX tabulars */ |
151 |
if (isdigit(optarg[0])) { |
152 |
conf->lines = atoi(optarg); |
153 |
} else { |
154 |
fprintf(stderr, |
155 |
"option \"lines\" need a positive integer value\n"); |
156 |
exit(EXIT_FAILURE); |
157 |
} |
158 |
break; |
159 |
case 'n': |
160 |
conf->header = 0; |
161 |
break; |
162 |
case 'x': |
163 |
conf->escape = 0; |
164 |
break; |
165 |
case 'v': /* version */ |
166 |
printf ("%s © 2002- Benoît Rouits <brouits@free.fr>\n" |
167 |
"\tVersion %s (%s)\n", PACKAGE, VERSION, RELEASE_DATE); |
168 |
exit (EXIT_SUCCESS); |
169 |
break; |
170 |
case 'p': /* LaTeX position in cell */ |
171 |
conf->pos = optarg[0]; /* position char in cell */ |
172 |
break; |
173 |
case 's': /* csv block separator */ |
174 |
if (optarg[0] == 'c') |
175 |
conf->sep = ','; |
176 |
else if (optarg[0] == 's') |
177 |
conf->sep = ';'; |
178 |
else if (optarg[0] == 't') |
179 |
conf->sep = '\t'; |
180 |
else if (optarg[0] == 'p') |
181 |
conf->sep = ' '; |
182 |
else if (optarg[0] == 'l') |
183 |
conf->sep = ':'; |
184 |
break; |
185 |
case 'c': /* color rows (thx to <jcorso@cse.Buffalo.EDU>) */ |
186 |
if (isdigit(optarg[0])) { |
187 |
conf->clrrow = (char*)malloc(strlen(optarg)+1); |
188 |
strcpy(conf->clrrow, optarg); |
189 |
} else { |
190 |
fprintf(stderr, |
191 |
"option \"colorrows\" needs a real value between 0 and 1\n"); |
192 |
exit(EXIT_FAILURE); |
193 |
} |
194 |
break; |
195 |
case 'r': /* reduce table size (original idea thx to <boaz.gezer@gmail.com>) */ |
196 |
if (isdigit(optarg[0])) { |
197 |
tmp = atoi(optarg); |
198 |
conf->red = (tmp>4) ? 4 : (tmp<0) ? 0 : tmp; /* [1-4] */ |
199 |
} else { |
200 |
fprintf(stderr, |
201 |
"option \"reduce\" needs an integer value between 1 and 4\n"); |
202 |
exit(EXIT_FAILURE); |
203 |
} |
204 |
break; |
205 |
case 'e': /*repeat table header for each table section*/ |
206 |
conf->repeat = 1; |
207 |
break; |
208 |
case 'y': /*don't draw vlines between columns*/ |
209 |
conf->vlines = 0; |
210 |
break; |
211 |
case 'z': /*don't draw hlines between rows*/ |
212 |
conf->hlines = 0; |
213 |
break; |
214 |
case 'a': /*landscape*/ |
215 |
conf->land = 1; |
216 |
break; |
217 |
case 'f': /*output font size*/ |
218 |
if (isdigit(optarg[0])) { |
219 |
conf->font = atoi(optarg); |
220 |
} else { |
221 |
fprintf(stderr, |
222 |
"option \"font\" need a positive integer value\n"); |
223 |
exit(EXIT_FAILURE); |
224 |
} |
225 |
break; |
226 |
} |
227 |
} |
228 |
return conf; |
229 |
} |
230 |
|
231 |
int guessCSV(config* conf, FILE* in) { |
232 |
/* guess the block delimiter and the csv separator */ |
233 |
int token; |
234 |
|
235 |
token = getc(in); /* first char is block delimiter */ |
236 |
if (token == EOF) { |
237 |
fprintf(stderr, "ERROR: empty file ?\n"); |
238 |
return -1; |
239 |
} else if (ispunct(token) || token == ' ') { |
240 |
/* found first block delimiter, act this way */ |
241 |
conf->block = token; |
242 |
fprintf(stderr, "Guessed '%c' as Block Delimiter\n", |
243 |
conf->block); |
244 |
/* stream file while token is printable data */ |
245 |
while ((token = getc(in)) != conf->block && |
246 |
token != '\n' && |
247 |
token != EOF) |
248 |
{/* getc has been done */} |
249 |
if (token == conf->block) { |
250 |
/* second delimiter : next is separator */ |
251 |
conf->sep = getc(in); |
252 |
fprintf(stderr, "Guessed '%c' as Separator\n", |
253 |
conf->sep); |
254 |
return 0; |
255 |
} else { |
256 |
return -1; /* what else ? */ |
257 |
} |
258 |
} else { /* no block delimiter, act this way */ |
259 |
conf->block = 0; |
260 |
fprintf(stderr, "Guessed No Block Delimiter\n"); |
261 |
/* stream file while input is not a control char */ |
262 |
while (!ispunct((token = getc(in))) && |
263 |
token != '\n' && |
264 |
token != EOF) |
265 |
{/* getc has been done */} |
266 |
/* guess CSV separator */ |
267 |
if (ispunct(token) || token == '\t' || token == ' ') { |
268 |
conf->sep = token; |
269 |
fprintf(stderr, "Guessed %c as Separator\n", conf->sep); |
270 |
return 0; |
271 |
} else { /* did not found any separator */ |
272 |
fprintf(stderr, "ERROR: Did not guess any Separator!\n"); |
273 |
return -1; |
274 |
} |
275 |
} |
276 |
return 0; |
277 |
} |
278 |
|
279 |
void getMaximums(config* conf, FILE* in) { |
280 |
/* gets the number of cols and chars of a csv file assuming a separator */ |
281 |
int token = 0; |
282 |
int nosep = 0; |
283 |
unsigned int curcol = 0; |
284 |
unsigned int curchar = 0; |
285 |
unsigned int inblock = 0; |
286 |
/* init */ |
287 |
conf->chars = 0; |
288 |
conf->cols = 0; |
289 |
conf->rows = 0; |
290 |
|
291 |
while (token != EOF) { |
292 |
token = getc(in); |
293 |
|
294 |
/* EOF ? */ |
295 |
if (token == EOF) { |
296 |
continue; |
297 |
} |
298 |
|
299 |
/* decide the maximums */ |
300 |
if (token == '\n') { |
301 |
curcol++; |
302 |
conf->cols = (conf->cols<curcol) ? curcol : conf->cols; |
303 |
conf->chars = (conf->chars<curchar) ? curchar : conf->chars; |
304 |
conf->rows++; |
305 |
curcol = 0; |
306 |
curchar = 0; |
307 |
inblock = 0; /* reset block state */ |
308 |
continue; |
309 |
} |
310 |
|
311 |
/* check implicit, non-guessed, block */ |
312 |
if (token == '\"') { |
313 |
if (nosep == 0) |
314 |
nosep = 1; |
315 |
else |
316 |
nosep = 0; |
317 |
} |
318 |
|
319 |
/* enter/quit a block */ |
320 |
if (conf->block && token == conf->block) { |
321 |
inblock = !inblock; |
322 |
continue; |
323 |
} |
324 |
|
325 |
/* count cols in current line */ |
326 |
if (token == conf->sep && ((conf->block && !inblock) || !conf->block) && nosep == 0) { |
327 |
curcol++; |
328 |
continue; |
329 |
} |
330 |
|
331 |
/* count chars in current cell */ |
332 |
if (token != conf->block && ((conf->block && inblock) || !conf->block)) { |
333 |
curchar++; |
334 |
continue; |
335 |
} |
336 |
} |
337 |
return; |
338 |
} |
339 |
|
340 |
unsigned int guessLinesPerPage(unsigned int font, double height) { |
341 |
/** |
342 |
* return: estimated number of lines per page, given page height in milimeter and font size in point |
343 |
*/ |
344 |
return height / (PTOMM(SKIPR * font)); |
345 |
} |
346 |
|
347 |
|
348 |
void doTeXsub(config* conf, char newsep, FILE* in, FILE* out) { |
349 |
/* substitutes CSV sep by LaTeX newsep and some TeX code */ |
350 |
int token = 0; |
351 |
int max; |
352 |
int numcols; |
353 |
unsigned int lines; |
354 |
int inblock = 0; |
355 |
int csvrows; |
356 |
int firstrow = 1; |
357 |
int nosep = 0; |
358 |
int token1 = 0; |
359 |
int token2 = 0; |
360 |
char headerrow[1000]; |
361 |
headerrow[0] = '\0'; |
362 |
|
363 |
max = numcols = conf->cols; |
364 |
csvrows = conf->rows; |
365 |
|
366 |
/* optimize a bit lines per tabular */ |
367 |
if (!conf->longtable && !conf->lines) { |
368 |
conf->lines = guessLinesPerPage(conf->font ? conf->font : 10, conf->land ? (HEIGHT / 1.414) : HEIGHT); |
369 |
fprintf(stderr, "guessed %d lines per page\n", conf->lines); |
370 |
} |
371 |
|
372 |
/* choose infinity when conf->lines is 0 */ |
373 |
lines = (conf->lines) ? conf->lines : MAXUINT; |
374 |
|
375 |
while (token != EOF) { |
376 |
token2 = token1; /* second last character, used for detection of quotation marks */ |
377 |
token1 = token; /* last character, used for detection of quotation marks */ |
378 |
token = getc(in); |
379 |
|
380 |
/* EOF ? */ |
381 |
if (token == EOF) { |
382 |
continue; |
383 |
} |
384 |
|
385 |
/* new line ? */ |
386 |
if (token == '\n') { |
387 |
inblock = 0; /* close block if any */ |
388 |
/* fill empty cols if any */ |
389 |
while (numcols > 1) { |
390 |
putc(newsep, out); |
391 |
numcols--; |
392 |
} |
393 |
if (!(firstrow && (conf->longtable && conf->repeat))) { |
394 |
fprintf(out, "\\\\\n"); /* TeX new line */ |
395 |
} else { /* first row and repeat and longtable */ |
396 |
fprintf(out, "%s\\\\\n", headerrow); |
397 |
} |
398 |
if (conf->hlines) { |
399 |
fprintf(out, "\\hline\n"); /* TeX draw hline */ |
400 |
} |
401 |
if (firstrow && (conf->longtable && conf->repeat)) { |
402 |
fprintf(out, "%s", "\\endhead\n"); |
403 |
} |
404 |
if (firstrow && conf->repeat) { |
405 |
char tmp[12]; |
406 |
sprintf(tmp, (conf->hlines ? "\\\\\n\\hline" : "\\\\\n")); |
407 |
strcat(headerrow, tmp); |
408 |
} |
409 |
firstrow = 0; |
410 |
numcols = max; /* reset numcols */ |
411 |
lines--; |
412 |
csvrows--; |
413 |
/* put a colored row or not (alternate) */ |
414 |
if (conf->clrrow && (lines % 2)) { |
415 |
fprintf(out, "\\colorrow "); |
416 |
} |
417 |
/* if the LaTeX tabular is full create a new one, except if no more row or this is a long table */ |
418 |
if (!lines && csvrows && !conf->longtable) { |
419 |
fprintf(out, "\\end{tabular}\n"); |
420 |
fprintf(out, "\\newline\n"); |
421 |
fprintf(out, "\\begin{tabular}{"); |
422 |
if (conf->vlines) { |
423 |
putc('|', out); |
424 |
} |
425 |
while (numcols--) { |
426 |
putc(conf->pos, out); |
427 |
if (conf->vlines) { |
428 |
putc('|', out); |
429 |
} |
430 |
} |
431 |
fprintf(out, "}\n"); |
432 |
if (conf->hlines) { |
433 |
fprintf(out, "\\hline\n"); |
434 |
} |
435 |
if (conf->repeat && !conf->longtable) { |
436 |
fprintf(out, "%s", headerrow); |
437 |
putc('\n', out); |
438 |
} |
439 |
numcols = max; |
440 |
lines = (conf->lines) ? conf->lines : MAXUINT; |
441 |
} |
442 |
/* else end of CSV data */ |
443 |
continue; |
444 |
} |
445 |
|
446 |
/* if commas in cells */ |
447 |
/* thx to <florian@heinze.at> */ |
448 |
if (token == '\"') { |
449 |
if (nosep == 0) |
450 |
nosep = 1; |
451 |
else |
452 |
nosep = 0; |
453 |
} |
454 |
|
455 |
/* new column ? */ |
456 |
if ((token == conf->sep && ((conf->block && !inblock) || !conf->block)) && nosep == 0) { |
457 |
if (!(firstrow && (conf->longtable && conf->repeat))) |
458 |
putc(newsep, out); |
459 |
numcols--; |
460 |
if (firstrow && conf->repeat) |
461 |
{ |
462 |
char tmp[2]; |
463 |
tmp[0] = newsep; |
464 |
tmp[1] = '\0'; |
465 |
strcat(headerrow, tmp); |
466 |
} |
467 |
continue; |
468 |
} |
469 |
|
470 |
/* enter/quit a block ? */ |
471 |
if (conf->block && token == conf->block) { |
472 |
inblock = !inblock; |
473 |
continue; |
474 |
} |
475 |
|
476 |
/* data ? */ |
477 |
if ((token != conf->block && ((conf->block && inblock) || !conf->block)) |
478 |
&& ((token == '\"' && token1 == '\"' && token2 == '\"') || token != '\"')) { |
479 |
/* look for special TeX char to escape */ |
480 |
/* FIXME: put all that into a subroutine */ |
481 |
int i = 0; |
482 |
if (conf->escape) |
483 |
for (i = 0; i < conf->tex->size; i++) { |
484 |
if (token == conf->tex->tab[i]) { |
485 |
switch (token) { |
486 |
case '\\': |
487 |
fprintf(out, "\\textbackslash{}"); |
488 |
if (firstrow && conf->repeat) |
489 |
{ |
490 |
char tmp[17]; |
491 |
sprintf(tmp, "\\textbackslash{}"); |
492 |
strcat(headerrow, tmp); |
493 |
} |
494 |
break; |
495 |
default: |
496 |
fprintf(out, "\\%c", token); |
497 |
if (firstrow && conf->repeat) |
498 |
{ |
499 |
char tmp[3]; |
500 |
tmp[0] = '\\'; |
501 |
tmp[1] = token; |
502 |
tmp[2] = '\0'; |
503 |
strcat(headerrow, tmp); |
504 |
} |
505 |
break; |
506 |
} |
507 |
break; /* there was some escaping */ |
508 |
} |
509 |
} |
510 |
/* or print raw char */ |
511 |
if ((i >= conf->tex->size) || (!conf->escape)) { |
512 |
if (!(firstrow && (conf->longtable && conf->repeat))) |
513 |
putc(token, out); /* do not print the header twice */ |
514 |
if (firstrow && conf->repeat) |
515 |
{ |
516 |
char tmp[2]; |
517 |
tmp[0] = token; |
518 |
tmp[1] = '\0'; |
519 |
strcat(headerrow, tmp); |
520 |
} |
521 |
} |
522 |
continue; |
523 |
} |
524 |
/* do nothing if unexpected char: just loop */ |
525 |
} |
526 |
return; |
527 |
} |
528 |
|
529 |
void doTeXdoc(config* conf, FILE* in, FILE* out) { |
530 |
/* prepares the LaTeX tabular layout */ |
531 |
int maxcols; |
532 |
int numcols; |
533 |
char* relsize[5] = {"0", "0.5", "1", "2", "4"}; /* LaTeX relsize good values */ |
534 |
char* tabcolsep[5] = {"0", "0.05", "0.1", "0.2", "0.4"}; /* LaTeX tabcolsep good values */ |
535 |
|
536 |
numcols = maxcols = conf->cols; |
537 |
if (conf->header) { |
538 |
if (conf->land) { |
539 |
if (conf->font) |
540 |
fprintf(out, "\\documentclass[%dpt,a4paper,landscape]{article}\n", conf->font); |
541 |
else { |
542 |
fprintf(out, "\\documentclass[a4paper,landscape]{article}\n"); |
543 |
conf->font = 10; /* pt */ |
544 |
} |
545 |
} else { |
546 |
if (conf->font) |
547 |
fprintf(out, "\\documentclass[%dpt,a4paper]{article}\n", conf->font); |
548 |
else { |
549 |
fprintf(out, "\\documentclass[a4paper]{article}\n"); |
550 |
conf->font = 10; /* pt */ |
551 |
} |
552 |
} |
553 |
fprintf(out, "\\usepackage[T1]{fontenc}\n"); |
554 |
fprintf(out, "\\usepackage[utf8]{inputenc}\n"); |
555 |
if (conf->red) { |
556 |
fprintf(out, "\\usepackage{relsize}\n"); |
557 |
} |
558 |
if (conf->clrrow) { |
559 |
fprintf(out, "\\usepackage{colortbl}\n"); |
560 |
} |
561 |
if (conf->longtable) { |
562 |
fprintf(out, "\\usepackage{longtable}\n"); |
563 |
} |
564 |
fprintf(out, "\\begin{document}\n"); |
565 |
} |
566 |
if (conf->clrrow) { |
567 |
fprintf(out, "\\def\\colorrow{\\rowcolor[gray]{%s}}\n", |
568 |
conf->clrrow); |
569 |
} |
570 |
if (conf->red) { |
571 |
fprintf(out, "\\relsize{-%s}\n", relsize[conf->red]); |
572 |
fprintf(out, "\\addtolength\\tabcolsep{-%sem}\n", tabcolsep[conf->red]); |
573 |
} |
574 |
if (conf->longtable) |
575 |
{ |
576 |
fprintf(out, "\\begin{longtable}{"); |
577 |
if (conf->vlines) |
578 |
putc('|', out); |
579 |
} |
580 |
else |
581 |
{ |
582 |
fprintf(out, "\\begin{tabular}{"); |
583 |
if (conf->vlines) |
584 |
putc('|', out); |
585 |
} |
586 |
while (numcols--) |
587 |
{ |
588 |
fprintf(out, "%c", conf->pos); /* position in cell */ |
589 |
if (conf->vlines) |
590 |
putc('|', out); |
591 |
} |
592 |
fprintf(out, "}\n"); |
593 |
if (conf->hlines) |
594 |
fprintf(out, "\\hline\n"); |
595 |
doTeXsub(conf, '&', in, out); /* & is LaTeX separator */ |
596 |
if (conf->longtable) { |
597 |
fprintf(out, "\\end{longtable}\n"); |
598 |
} else { |
599 |
fprintf(out, "\\end{tabular}\n"); |
600 |
} |
601 |
if (conf->red) { |
602 |
fprintf(out, "\\addtolength\\tabcolsep{+%sem}\n", tabcolsep[conf->red]); |
603 |
fprintf(out, "\\relsize{+%s}\n", relsize[conf->red]); |
604 |
} |
605 |
if (conf->header) { |
606 |
fprintf(out, "\\end{document}\n"); |
607 |
} |
608 |
return; |
609 |
} |
610 |
|
611 |
int main (int argc, char **argv) { |
612 |
FILE* fp; |
613 |
config* conf; |
614 |
|
615 |
extern int optind, opterr, optopt; |
616 |
|
617 |
conf = (config*)malloc(sizeof(config)); |
618 |
/* defaults (ensure init): */ |
619 |
conf->cols = 1; /* CSV: if getMaximums fails */ |
620 |
conf->rows = 0; /* CSV: must be 0 */ |
621 |
conf->chars = 0; /* CSV: must be 0 */ |
622 |
conf->pos = 'l'; /* usual; LaTeX */ |
623 |
conf->lines = 40; /* usual; LaTeX */ |
624 |
conf->guess = 0; /* usual */ |
625 |
conf->sep = ','; /* default; csv */ |
626 |
conf->block = 0; /* default; csv */ |
627 |
conf->header = 1; /* usual; LaTeX */ |
628 |
conf->escape = 1; /* usual; LaTeX */ |
629 |
conf->clrrow = NULL; /* default; LaTeX */ |
630 |
conf->red = 0; /* default; LaTeX */ |
631 |
conf->longtable = 0; /* default; without package longtable */ |
632 |
conf->repeat = 0; /* default; do not repeat the header row */ |
633 |
conf->vlines = 1; /* default; draw lines between columns */ |
634 |
conf->hlines = 1; /* default; draw lines between rows */ |
635 |
conf->font = 0; /* default to LaTeX defaults */ |
636 |
conf->land = 0; /* default to LaTeX defaults */ |
637 |
|
638 |
/* TeX charaters to escape */ |
639 |
conf->tex = (texcape*)malloc(sizeof(texcape)); |
640 |
conf->tex->tab = "\\_#$%^&{}~"; |
641 |
conf->tex->size = strlen(conf->tex->tab); |
642 |
|
643 |
|
644 |
conf = parseOptions(conf, argc, argv); |
645 |
|
646 |
if (optind == argc) { |
647 |
/* copy stdin into tmp file */ |
648 |
int c; |
649 |
fp = tmpfile(); |
650 |
while (EOF != (c = getc(stdin))) { |
651 |
putc(c, fp); |
652 |
} |
653 |
rewind(fp); |
654 |
} else { |
655 |
fp = fopen(argv[optind], "r"); |
656 |
} |
657 |
|
658 |
if (!fp) { |
659 |
fprintf(stderr, "Can't open file %s\n", argv[optind]); |
660 |
exit(EXIT_FAILURE); |
661 |
} |
662 |
|
663 |
if (conf->guess) { |
664 |
if (guessCSV(conf, fp)) { |
665 |
fprintf(stderr, "Please run again by using --delimiter (if any) and --separator\n"); |
666 |
fclose(fp); |
667 |
exit(EXIT_FAILURE); |
668 |
} |
669 |
rewind(fp); |
670 |
} |
671 |
|
672 |
getMaximums(conf, fp); |
673 |
rewind(fp); |
674 |
|
675 |
doTeXdoc(conf, fp, stdout); |
676 |
|
677 |
free(conf->tex); |
678 |
if (conf->clrrow) |
679 |
free(conf->clrrow); |
680 |
free(conf); |
681 |
fclose(fp); |
682 |
|
683 |
exit(EXIT_SUCCESS); |
684 |
} |