ascii: Add support for multibyte characters.
[pspp-builds.git] / tests / output / render-test.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011 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 <errno.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "libpspp/assertion.h"
26 #include "libpspp/compiler.h"
27 #include "libpspp/string-map.h"
28 #include "output/ascii.h"
29 #include "output/driver.h"
30 #include "output/tab.h"
31 #include "output/table-item.h"
32
33 #include "gl/error.h"
34 #include "gl/progname.h"
35 #include "gl/xalloc.h"
36 #include "gl/xvasprintf.h"
37
38 /* --transpose: Transpose the table before outputting? */
39 static int transpose;
40
41 /* --emphasis: ASCII driver emphasis option. */
42 static char *emphasis;
43
44 /* --box: ASCII driver box option. */
45 static char *box;
46
47 /* --draw-mode: special ASCII driver test mode. */
48 static int draw_mode;
49
50 /* ASCII driver, for ASCII driver test mode. */
51 static struct output_driver *ascii_driver;
52
53 static const char *parse_options (int argc, char **argv);
54 static void usage (void) NO_RETURN;
55 static struct table *read_table (FILE *);
56 static void draw (FILE *);
57
58 int
59 main (int argc, char **argv)
60 {
61   const char *input_file_name;
62   FILE *input;
63
64   set_program_name (argv[0]);
65   input_file_name = parse_options (argc, argv);
66
67   if (!strcmp (input_file_name, "-"))
68     input = stdin;
69   else
70     {
71       input = fopen (input_file_name, "r");
72       if (input == NULL)
73         error (1, errno, "%s: open failed", input_file_name);
74     }
75
76   if (!draw_mode)
77     {
78       struct table *table;
79
80       table = read_table (input);
81
82       if (transpose)
83         table = table_transpose (table);
84
85       table_item_submit (table_item_create (table, NULL));
86     }
87   else
88     draw (input);
89
90   if (input != stdin)
91     fclose (input);
92
93   output_close ();
94
95   return 0;
96 }
97
98 static void
99 configure_drivers (int width, int length)
100 {
101   struct string_map options, tmp;
102   struct output_driver *driver;
103
104   string_map_init (&options);
105   string_map_insert (&options, "format", "txt");
106   string_map_insert (&options, "output-file", "-");
107   string_map_insert_nocopy (&options, xstrdup ("width"),
108                             xasprintf ("%d", width));
109   string_map_insert_nocopy (&options, xstrdup ("length"),
110                             xasprintf ("%d", length));
111   if (emphasis != NULL)
112     string_map_insert (&options, "emphasis", emphasis);
113   if (box != NULL)
114     string_map_insert (&options, "box", box);
115
116   /* Render to stdout. */
117   string_map_clone (&tmp, &options);
118   ascii_driver = driver = output_driver_create (&tmp);
119   if (driver == NULL)
120     exit (EXIT_FAILURE);
121   output_driver_register (driver);
122   string_map_destroy (&tmp);
123
124   if (draw_mode)
125     return;
126
127   /* Render to render.txt. */
128   string_map_replace (&options, "output-file", "render.txt");
129   driver = output_driver_create (&options);
130   if (driver == NULL)
131     exit (EXIT_FAILURE);
132   output_driver_register (driver);
133
134 #ifdef HAVE_CAIRO
135   /* Render to render.pdf. */
136   string_map_insert (&options, "output-file", "render.pdf");
137   string_map_insert (&options, "top-margin", "0");
138   string_map_insert (&options, "bottom-margin", "0");
139   string_map_insert (&options, "left-margin", "0");
140   string_map_insert (&options, "right-margin", "0");
141   string_map_insert_nocopy (&options, xstrdup ("paper-size"),
142                             xasprintf ("%dx%dpt", width * 5, length * 8));
143   driver = output_driver_create (&options);
144   if (driver == NULL)
145     exit (EXIT_FAILURE);
146   output_driver_register (driver);
147 #endif
148
149   string_map_insert (&options, "output-file", "render.odt");
150   driver = output_driver_create (&options);
151   if (driver == NULL)
152     exit (EXIT_FAILURE);
153   output_driver_register (driver);
154
155   string_map_destroy (&options);
156 }
157
158 static const char *
159 parse_options (int argc, char **argv)
160 {
161   int width = 79;
162   int length = 66;
163
164   for (;;)
165     {
166       enum {
167         OPT_WIDTH = UCHAR_MAX + 1,
168         OPT_LENGTH,
169         OPT_EMPHASIS,
170         OPT_BOX,
171         OPT_HELP
172       };
173       static const struct option options[] =
174         {
175           {"width", required_argument, NULL, OPT_WIDTH},
176           {"length", required_argument, NULL, OPT_LENGTH},
177           {"transpose", no_argument, &transpose, 1},
178           {"emphasis", required_argument, NULL, OPT_EMPHASIS},
179           {"box", required_argument, NULL, OPT_BOX},
180           {"draw-mode", no_argument, &draw_mode, 1},
181           {"help", no_argument, NULL, OPT_HELP},
182           {NULL, 0, NULL, 0},
183         };
184
185       int c = getopt_long (argc, argv, "", options, NULL);
186       if (c == -1)
187         break;
188
189       switch (c)
190         {
191         case OPT_WIDTH:
192           width = atoi (optarg);
193           break;
194
195         case OPT_LENGTH:
196           length = atoi (optarg);
197           break;
198
199         case OPT_EMPHASIS:
200           emphasis = optarg;
201           break;
202
203         case OPT_BOX:
204           box = optarg;
205           break;
206
207         case OPT_HELP:
208           usage ();
209
210         case 0:
211           break;
212
213         case '?':
214           exit(EXIT_FAILURE);
215           break;
216
217         default:
218           NOT_REACHED ();
219         }
220
221     }
222
223   configure_drivers (width, length);
224
225   if (optind + 1 != argc)
226     error (1, 0, "exactly one non-option argument required; "
227            "use --help for help");
228   return argv[optind];
229 }
230
231 static void
232 usage (void)
233 {
234   printf ("%s, to test rendering of PSPP tables\n"
235           "usage: %s [OPTIONS] INPUT\n"
236           "\nOptions:\n"
237           "  --width=WIDTH   set page width in characters\n"
238           "  --length=LINE   set page length in lines\n",
239           program_name, program_name);
240   exit (EXIT_SUCCESS);
241 }
242
243 static void
244 replace_newlines (char *p)
245 {
246   char *q;
247
248   for (q = p; *p != '\0'; )
249     if (*p == '\\' && p[1] == 'n')
250       {
251         *q++ = '\n';
252         p += 2;
253       }
254     else
255       *q++ = *p++;
256   *q = '\0';
257 }
258
259 static struct table *
260 read_table (FILE *stream)
261 {
262   struct tab_table *tab;
263   char buffer[1024];
264   int input[6];
265   int n_input = 0;
266   int nr, nc, hl, hr, ht, hb;
267   int r, c;
268
269   if (fgets (buffer, sizeof buffer, stream) == NULL
270       || (n_input = sscanf (buffer, "%d %d %d %d %d %d",
271                             &input[0], &input[1], &input[2],
272                             &input[3], &input[4], &input[5])) < 2)
273     error (1, 0, "syntax error reading row and column count");
274
275   nr = input[0];
276   nc = input[1];
277   hl = n_input >= 3 ? input[2] : 0;
278   hr = n_input >= 4 ? input[3] : 0;
279   ht = n_input >= 5 ? input[4] : 0;
280   hb = n_input >= 6 ? input[5] : 0;
281
282   tab = tab_create (nc, nr);
283   tab_headers (tab, hl, hr, ht, hb);
284   for (r = 0; r < nr; r++)
285     for (c = 0; c < nc; c++)
286       if (tab_cell_is_empty (tab, c, r))
287         {
288           char *new_line;
289           char *text;
290           int rs, cs;
291
292           if (fgets (buffer, sizeof buffer, stream) == NULL)
293             error (1, 0, "unexpected end of input reading row %d, column %d",
294                    r, c);
295           new_line = strchr (buffer, '\n');
296           if (new_line != NULL)
297             *new_line = '\0';
298
299           text = buffer;
300           if (sscanf (text, "%d*%d", &rs, &cs) == 2)
301             {
302               while (*text != ' ' && *text != '\0')
303                 text++;
304               if (*text == ' ')
305                 text++;
306             }
307           else
308             {
309               rs = 1;
310               cs = 1;
311             }
312
313           while (*text && strchr ("<>^,@", *text))
314             switch (*text++)
315               {
316               case '<':
317                 tab_vline (tab, TAL_1, c, r, r + rs - 1);
318                 break;
319
320               case '>':
321                 tab_vline (tab, TAL_1, c + cs, r, r + rs - 1);
322                 break;
323
324               case '^':
325                 tab_hline (tab, TAL_1, c, c + cs - 1, r);
326                 break;
327
328               case ',':
329                 tab_hline (tab, TAL_1, c, c + cs - 1, r + rs);
330                 break;
331
332               case '@':
333                 tab_box (tab, TAL_1, TAL_1, -1, -1, c, r,
334                          c + cs - 1, r + rs - 1);
335                 break;
336
337               default:
338                 NOT_REACHED ();
339               }
340
341           replace_newlines (text);
342
343           tab_joint_text (tab, c, r, c + cs - 1, r + rs - 1, 0, text);
344         }
345
346   if (getc (stream) != EOF)
347     error (1, 0, "unread data at end of input");
348
349   return &tab->table;
350 }
351
352 static void
353 draw (FILE *stream)
354 {
355   char buffer[1024];
356   int line = 0;
357
358   while (fgets (buffer, sizeof buffer, stream))
359     {
360       char text[sizeof buffer];
361       int emph;
362       int x, y;
363
364       line++;
365       if (strchr ("#\r\n", buffer[0]))
366         continue;
367
368       if (sscanf (buffer, "%d %d %d %[^\n]", &x, &y, &emph, text) != 4)
369         error (1, 0, "line %d has invalid format", line);
370
371       ascii_test_write (ascii_driver, text, x, y, emph ? TAB_EMPH : 0);
372     }
373 }