/[csv2latex]/csv2latex.c
ViewVC logotype

Annotation of /csv2latex.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Thu Oct 8 09:54:13 2009 UTC (10 years, 11 months ago) by ben
File MIME type: text/plain
File size: 14348 byte(s)
changed repository to herewe
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     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