/[csv2latex]/csv2latex.c
ViewVC logotype

Contents of /csv2latex.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Thu Oct 8 09:54:13 2009 UTC (10 years, 4 months ago) by ben
File MIME type: text/plain
File size: 14348 byte(s)
changed repository to herewe
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 char* clrrow; /* row graylevel (from 0 to 1) */
56 texcape* tex; /* TeX escapes */
57 } config;
58
59 #define MAXUINT ((unsigned int)(-1))
60
61 void rtfm(char* prog) {
62 printf("%s translates a csv file to a LaTeX file\n", basename(prog));
63 printf("Example: %s january_stats.csv > january_stats.tex\n", basename(prog));
64 printf("Usage: %s [--nohead] (LaTeX) no document header: useful for inclusion\n", basename(prog));
65 printf(" [--longtable] (LaTeX) use package longtable: useful for long input\n");
66 printf(" [--noescape] (LaTeX) do not escape text: useful for mixed CSV/TeX input\n");
67 printf(" [--guess] (CSV) guess separator and block |\n"
68 " [--separator <(c)omma|(s)emicolon|(t)ab|s(p)ace|co(l)on>] (CSV's comma)\n"
69 " [--block <(q)uote|(d)ouble|(n)one>] (CSV) block delimiter (e.g: none)\n");
70 printf(" [--lines n] (LaTeX) rows per table: useful for long tabulars\n");
71 printf(" [--position <l|c|r>] (LaTeX) text align in cells\n");
72 printf(" [--colorrows graylevel] (LaTeX) alternate gray rows (e.g: 0.75)\n");
73 printf(" [--reduce level] (LaTeX) reduce table size (e.g: 1)\n");
74 printf(" csv_file.csv\n");
75 printf("The \"longtable\" option needs the {longtable} LaTeX package\n");
76 printf("The \"colorrows\" option needs the {colortbl} LaTeX package\n");
77 printf("The \"reduce\" option needs the {relsize} LaTeX package\n");
78 return;
79 }
80
81 config* parseOptions (config* conf, int argc, char **argv) {
82 /* thx to <vfebvre@lautre.net> */
83 int opt;
84 int tmp;
85
86 #if defined USE_GETOPT
87 #else
88 int longopt_index = 0;
89 static struct option long_options[] = {
90 {"help", 0, NULL, 'h'},
91 {"guess", 0, NULL, 'g'},
92 {"block", 1, NULL, 'b'},
93 {"lines", 1, NULL, 'l'},
94 {"noescape", 0, NULL, 'x'},
95 {"nohead", 0, NULL, 'n'},
96 {"version", 0, NULL, 'v'},
97 {"position", 1, NULL, 'p'},
98 {"separator", 1, NULL, 's'},
99 {"colorrows", 1, NULL, 'c'},
100 {"reduce", 1, NULL, 'r'},
101 {"longtable", 0, NULL, 't'},
102 {NULL, 0, NULL, 0} /* marks end-of-list */
103 };
104 #endif
105 #if defined USE_GETOPT
106 while ((opt = getopt (argc, argv, "hvgnxt?b:l:p:s:c:r:")) != EOF) {
107 #else
108 while ((opt = getopt_long (argc, argv, "hvgnxt?b:l:p:s:c:r:", long_options, &longopt_index)) > 0) {
109 #endif
110 switch (opt) {
111 case '?':
112 case 'h':
113 rtfm (argv[0]);
114 exit (EXIT_SUCCESS);
115 break;
116 case 'g': /* guess the CSV */
117 conf->guess=1;
118 break;
119 case 't': /* use package longtable */ /* thanks to <Christof.Bodner@infineon.com> */
120 conf->longtable=1;
121 break;
122 case 'b': /* csv block delimiter */
123 if(optarg[0] == 'q')
124 conf->block = '\'';
125 else if(optarg[0] == 'd')
126 conf->block = '"';
127 else if(optarg[0] == 'n')
128 conf->block = 0; /* no block delimiter */
129 break;
130 case 'l': /* number of lines per TeX tabulars */
131 if(isdigit(optarg[0])) {
132 conf->lines=atoi(optarg);
133 } else {
134 fprintf(stderr,
135 "option \"lines\" need a positive integer value\n");
136 exit(EXIT_FAILURE);
137 }
138 break;
139 case 'n':
140 conf->header=0;
141 break;
142 case 'x':
143 conf->escape=0;
144 break;
145 case 'v': /* version */
146 printf ("%s © 2002- Benoît Rouits <brouits@free.fr>\n"
147 "\tVersion %s (%s)\n", PACKAGE, VERSION, RELEASE_DATE);
148 exit (EXIT_SUCCESS);
149 break;
150 case 'p': /* LaTeX position in cell */
151 conf->pos=optarg[0]; /* position char in cell */
152 break;
153 case 's': /* csv block separator */
154 if(optarg[0] == 'c')
155 conf->sep = ',';
156 else if(optarg[0] == 's')
157 conf->sep = ';';
158 else if(optarg[0] == 't')
159 conf->sep = '\t';
160 else if(optarg[0] == 'p')
161 conf->sep = ' ';
162 else if(optarg[0] == 'l')
163 conf->sep = ':';
164 break;
165 case 'c': /* color rows (thx to <jcorso@cse.Buffalo.EDU>) */
166 if(isdigit(optarg[0])) {
167 conf->clrrow = (char*)malloc(strlen(optarg)+1);
168 strcpy(conf->clrrow,optarg);
169 } else {
170 fprintf(stderr,
171 "option \"colorrows\" needs a real value between 0 and 1\n");
172 exit(EXIT_FAILURE);
173 }
174 break;
175 case 'r': /* reduce table size (original idea thx to <boaz.gezer@gmail.com>) */
176
177 if(isdigit(optarg[0])) {
178 tmp = atoi(optarg);
179 conf->red=(tmp>4)?4:(tmp<0)?0:tmp; /* [1-4] */
180 } else {
181 fprintf(stderr,
182 "option \"reduce\" needs an integer value between 1 and 4\n");
183 exit(EXIT_FAILURE);
184 }
185 break;
186 }
187 }
188 return conf;
189 }
190 int guessCSV(config* conf, FILE* in) {
191 /* guess the block delimiter and the csv separator */
192 int token;
193
194 token=getc(in); /* first char is block delimiter */
195 if(token == EOF) {
196 fprintf(stderr,"ERROR: emtpy file ?\n");
197 return(-1);
198 } else if (ispunct(token) || token == ' ') {
199 /* found first block delimiter, act this way */
200 conf->block=token;
201 fprintf(stderr,"Guessed '%c' as Block Delimiter\n",
202 conf->block);
203 /* stream file while token is printable data */
204 while((token=getc(in)) != conf->block &&
205 token != '\n' &&
206 token != EOF)
207 {/* getc has been done */}
208 if(token == conf->block){
209 /* second delimiter : next is separator */
210 conf->sep=getc(in);
211 fprintf(stderr,"Guessed '%c' as Separator\n",
212 conf->sep);
213 return(0);
214 }else{
215 return (-1); /* what else ? */
216 }
217 }else{ /* no block delimiter, act this way */
218 conf->block=0;
219 fprintf(stderr,"Guessed No Block Delimiter\n");
220 /* stream file while input is not a control char */
221 while(isalnum((token=getc(in))) &&
222 token != '\n' &&
223 token != EOF)
224 {/* getc has been done */}
225 /* guess CSV separator */
226 if(ispunct(token) || token == '\t' || token == ' '){
227 conf->sep=token;
228 fprintf(stderr,"Guessed %c as Separator\n", conf->sep);
229 return(0);
230 } else { /* did not found any separator */
231 fprintf(stderr,"ERROR: Did not guess any Separator!\n");
232 return(-1);
233 }
234 }
235 return(0);
236 }
237
238 void getMaximums(config* conf, FILE* in) {
239 /* gets the number of cols and chars of a csv file assuming a separator */
240 int token=0;
241 unsigned int curcol=0;
242 unsigned int curchar=0;
243 unsigned int inblock=0;
244 /* init */
245 conf->chars=0;
246 conf->cols=0;
247 conf->rows=0;
248
249 while (token != EOF) {
250 token=getc(in);
251
252 /* EOF ? */
253 if (token == EOF) {
254 continue;
255 }
256
257 /* decide the maximums */
258 if (token == '\n') {
259 curcol++;
260 conf->cols=(conf->cols<curcol)?curcol:conf->cols;
261 conf->chars=(conf->chars<curchar)?curchar:conf->chars;
262 conf->rows++;
263 curcol=0;
264 curchar=0;
265 inblock=0; /* reset block state */
266 continue;
267 }
268
269 /* enter/quit a block */
270 if (conf->block && token == conf->block) {
271 inblock=!inblock;
272 continue;
273 }
274
275 /* count cols in current line */
276 if (token == conf->sep && ((conf->block && !inblock) || !conf->block)) {
277 curcol++;
278 continue;
279 }
280
281 /* count chars in current cell */
282 if (token != conf->block && ((conf->block && inblock) || !conf->block)) {
283 curchar++;
284 continue;
285 }
286 }
287 return;
288 }
289
290 void doTeXsub(config* conf, char newsep, FILE* in, FILE* out) {
291 /* substitutes CSV sep by LaTeX newsep and some TeX code */
292 int token=0;
293 int max;
294 int numcols;
295 int lines;
296 int inblock=0;
297 int csvrows;
298
299 max=numcols=conf->cols;
300 csvrows=conf->rows;
301 /* choose infinity when conf->lines is 0 */
302 lines=(conf->lines)?conf->lines:MAXUINT;
303
304 while (token!=EOF) {
305 token=getc(in);
306
307 /* EOF ? */
308 if (token == EOF) {
309 continue;
310 }
311
312 /* new line ? */
313 if (token == '\n') {
314 inblock = 0; /* close block if any */
315 /* fill empty cols if any */
316 while (numcols > 1) {
317 putc(newsep,out);
318 numcols--;
319 }
320 fprintf(out,"\\\\\n"); /* TeX new line */
321 fprintf(out,"\\hline\n"); /* TeX draw hline */
322 numcols=max; /* reset numcols */
323 lines--;
324 csvrows--;
325 /* put a colored row or not (alternate) */
326 if (conf->clrrow && (lines % 2)){
327 fprintf(out,"\\colorrow ");
328 }
329 /* if the LaTeX tabular is full create a new one, except if no more row */
330 if (!lines && csvrows){
331 if (conf->longtable) {
332 fprintf(out,"\\end{longtable}\n");
333 fprintf(out, "\\newline\n");
334 fprintf(out,"\\begin{longtable}{|\n"); }
335 else {
336 fprintf(out,"\\end{tabular}\n");
337 fprintf(out, "\\newline\n");
338 fprintf(out,"\\begin{tabular}{|"); }
339 while(numcols--)
340 fprintf(out, "%c|", conf->pos);
341 fprintf(out, "}\n");
342 fprintf(out, "\\hline\n");
343 numcols=max;
344 lines=(conf->lines)?conf->lines:MAXUINT;
345 } /* else end of CSV data */
346 continue;
347 }
348
349 /* new column ? */
350 if (token == conf->sep && ((conf->block && !inblock) || !conf->block)) {
351 putc(newsep,out);
352 numcols--;
353 continue;
354 }
355
356 /* enter/quit a block ? */
357 if (conf->block && token == conf->block) {
358 inblock=!inblock;
359 continue;
360 }
361
362 /* data ? */
363 if (token != conf->block && ((conf->block && inblock) || !conf->block)) {
364 /* look for special TeX char to escape */
365 /* FIXME: put all that into a subroutine */
366 int i=0;
367 if (conf->escape)
368 for (i=0;i < conf->tex->size; i++) {
369 if (token == conf->tex->tab[i]) {
370 switch (token) {
371 case '\\':
372 fprintf(out, "\\textbackslash{}");
373 break;
374 default:
375 fprintf(out, "\\%c", token);
376 break;
377 }
378 break; /* there was some escaping */
379 }
380 }
381 /* or print raw char */
382 if ( (i >= conf->tex->size) || (!conf->escape) ) {
383 putc(token, out);
384 }
385 continue;
386 }
387 /* do nothing if unexpected char: just loop */
388 }
389 return;
390 }
391
392 void doTeXdoc(config* conf, FILE* in, FILE* out) {
393 /* prepares the LaTeX tabular layout */
394 int maxcols;
395 int numcols;
396 char* relsize[5] = {"0", "0.5", "1", "2", "4"}; /* LaTeX relsize good values */
397 char* tabcolsep[5] = {"0", "0.05", "0.1", "0.2", "0.4"}; /* LaTeX tabcolsep good values */
398
399 numcols=maxcols=conf->cols;
400 if(conf->header){
401 fprintf(out, "\\documentclass[a4paper]{article}\n");
402 fprintf(out, "\\usepackage[T1]{fontenc}\n");
403 fprintf(out, "\\usepackage[latin1]{inputenc}\n");
404 if (conf->red){
405 fprintf(out,"\\usepackage{relsize}\n");
406 }
407 if (conf->clrrow){
408 fprintf(out,"\\usepackage{colortbl}\n");
409 }
410 if (conf->longtable){
411 fprintf(out,"\\usepackage{longtable}\n");
412 }
413 fprintf(out, "\\begin{document}\n");
414 }
415 if (conf->clrrow){
416 fprintf(out,"\\def\\colorrow{\\rowcolor[gray]{%s}}\n",
417 conf->clrrow);
418 }
419 if (conf->red){
420 fprintf(out,"\\relsize{-%s}\n", relsize[conf->red]);
421 fprintf(out,"\\addtolength\\tabcolsep{-%sem}\n", tabcolsep[conf->red]);
422 }
423 if (conf->longtable)
424 fprintf(out, "\\begin{longtable}{|");
425 else
426 fprintf(out, "\\begin{tabular}{|");
427 while(numcols--)
428 fprintf(out, "%c|",conf->pos); /* position in cell */
429 fprintf(out, "}\n");
430 fprintf(out, "\\hline\n");
431 doTeXsub(conf, '&', in, out); /* & is LaTeX separator */
432 if (conf->longtable) {
433 fprintf(out, "\\end{longtable}\n");
434 } else {
435 fprintf(out, "\\end{tabular}\n");
436 }
437 if (conf->red){
438 fprintf(out,"\\addtolength\\tabcolsep{+%sem}\n", tabcolsep[conf->red]);
439 fprintf(out,"\\relsize{+%s}\n", relsize[conf->red]);
440 }
441 if(conf->header){
442 fprintf(out, "\\end{document}\n");
443 }
444 return;
445 }
446
447 int main (int argc, char **argv) {
448 FILE* fp;
449 config* conf;
450
451 extern int optind, opterr, optopt;
452
453 if(argc == 1){
454 rtfm(argv[0]);
455 exit(EXIT_SUCCESS);
456 }
457 conf=(config*)malloc(sizeof(config));
458 /* defaults (ensure init): */
459 conf->cols=1; /* CSV: if getMaximums fails */
460 conf->rows=0; /* CSV: must be 0 */
461 conf->chars=0; /* CSV: must be 0 */
462 conf->pos='l'; /* usual; LaTeX */
463 conf->lines=40; /* usual; LaTeX */
464 conf->guess=0; /* usual */
465 conf->sep=','; /* default; csv */
466 conf->block=0; /* default; csv */
467 conf->header=1; /* usual; LaTeX */
468 conf->escape=1; /* usual; LaTeX */
469 conf->clrrow=NULL; /* default; LaTeX */
470 conf->red=0; /* default; LaTeX */
471 conf->longtable=0; /* default; without package longtable */
472
473 /* TeX charaters to escape */
474 conf->tex=(texcape*)malloc(sizeof(texcape));
475 conf->tex->tab = "\\_#$%^&{}~";
476 conf->tex->size = 10;
477
478 conf=parseOptions(conf, argc, argv);
479 fp=fopen(argv[optind],"r");
480 if (!fp){
481 fprintf(stderr,"Can't open file %s\n", argv[optind]);
482 exit(EXIT_FAILURE);
483 }
484 if(conf->guess){
485 if(guessCSV(conf, fp)){
486 fprintf(stderr,"Please run again by using -- delimiter (if any) and --separator\n");
487 exit(EXIT_FAILURE);
488 }
489 rewind(fp);
490 }
491 getMaximums(conf, fp);
492 rewind(fp);
493 doTeXdoc(conf, fp, stdout);
494 free(conf->tex);
495 if (conf->clrrow) free(conf->clrrow); conf->clrrow=NULL;
496 free(conf);
497 fclose(fp);
498 return 0;
499 }

  ViewVC Help
Powered by ViewVC 1.1.26