Add a TeX driver
[pspp] / tests / output / tex-strings.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2020 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "tex-rendering.h"
20 #include "tex-glyphs.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <assert.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <string.h>
28
29 #include <gl/xalloc.h>
30
31 static void
32 tex_render (FILE *fp, const char *str)
33 {
34   fputs (str, fp);
35   fputc ('\n', fp);
36 }
37
38 #define BLOCK_SIZE 16
39
40 /* Reads an entire file FP and returns it as a string.
41    Any single instance of newline will be mutated to a space.
42    However multiple consecutive newlines will be mutated to
43    a single newline.  */
44 static char *
45 read_whole_file (FILE *fp)
46 {
47   char *result = 0;
48   size_t bytes = 0;
49   size_t len = 0;
50   int c = -1;
51   int consecutive_nl = 0;
52   while ((c = fgetc (fp)) >= 0)
53     {
54       if (len <= bytes + 1)
55         {
56           result = xrealloc (result, len + BLOCK_SIZE);
57           memset (result + len, 0, BLOCK_SIZE);
58           len += BLOCK_SIZE;
59         }
60       if (c != '\n')
61         {
62           if (consecutive_nl > 1)
63             result[bytes++] = '\n';
64           if (consecutive_nl == 1)
65             result[bytes++] = ' ';
66
67           result[bytes++] = c;
68         }
69
70       if (c == '\n')
71         consecutive_nl++;
72       else
73         consecutive_nl = 0;
74     }
75
76   return result;
77 }
78
79 static long macro_insertion_point = 0;
80
81 static void
82 tex_preamble (FILE *fp, const char *str)
83 {
84   long where = ftell (fp);
85   fseek (fp, macro_insertion_point, SEEK_SET);
86   tex_render (fp, str);
87   fputc ('\n', fp);
88   macro_insertion_point = ftell (fp);
89   fseek (fp, where, SEEK_SET);
90 }
91
92
93 int
94 main (int argc, char **argv)
95 {
96   char *outfile = NULL;
97   int opt;
98   while ((opt = getopt (argc, argv, "o:")) != -1)
99     {
100       switch (opt)
101         {
102         case 'o':
103           outfile = argv[optind-1];
104           break;
105         default:
106           fprintf (stderr, "Usage: tex-strings  -o <outfile> <infile1> <infile2> ... <infileN>\n");
107           return 1;
108         }
109     }
110
111   if (optind >= argc)
112     {
113       fprintf (stderr, "Usage: tex-strings  -o <outfile> <infile1> <infile2> ... <infileN>\n");
114       return 1;
115     }
116
117   FILE *fpout = fopen (outfile, "w");
118   if (!fpout)
119     {
120       int err = errno;
121       fprintf (stderr, "Cannot open output file %s: %s\n", outfile, strerror (err));
122       return 1;
123     }
124
125   struct hmap macros;
126   hmap_init (&macros);
127
128   fseek (fpout, 4096, SEEK_SET);
129
130   for (int arg = optind; arg < argc; ++arg)
131   {
132     FILE *fpin = fopen (argv[arg], "r");
133     if (!fpin)
134       {
135         int err = errno;
136         fprintf (stderr, "Cannot open input file %s: %s\n", argv[arg], strerror (err));
137         return 1;
138       }
139
140     tex_render (fpout, "\\noindent");
141
142     char *str = read_whole_file (fpin);
143     const char *s = str;
144     size_t n = strlen (str);
145     const char *frag = 0;
146     while (n > 0)
147       {
148         frag = u8_to_tex_fragments (&s, &n, &macros);
149         fputs (frag, fpout);
150       }
151     free (str);
152
153
154     fclose (fpin);
155
156     tex_render(fpout, "\\par\\vskip 1em");
157   }
158
159   {
160     struct tex_macro *m;
161     struct tex_macro *next;
162     HMAP_FOR_EACH_SAFE (m, next, struct tex_macro, node, &macros)
163       {
164         tex_preamble (fpout, tex_macro[m->index]);
165         free (m);
166       }
167   }
168   hmap_destroy (&macros);
169
170   tex_render (fpout, "\\bye");
171
172   fclose (fpout);
173   return 0;
174 }