7890470d5fe548fe01da5e8fc5283551335da3b8
[pspp] / tests / output / pivot-table-test.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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 <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "data/file-handle-def.h"
27 #include "language/lexer/lexer.h"
28 #include "language/lexer/format-parser.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/i18n.h"
32 #include "libpspp/string-map.h"
33 #include "output/driver.h"
34 #include "output/message-item.h"
35 #include "output/options.h"
36 #include "output/pivot-table.h"
37 #include "output/table-item.h"
38
39 #include "gl/error.h"
40 #include "gl/progname.h"
41 #include "gl/xalloc.h"
42 #include "gl/xvasprintf.h"
43
44 /* --emphasis: Enable emphasis in ASCII driver? */
45 static bool emphasis;
46
47 /* --box: ASCII driver box option. */
48 static char *box;
49
50 /* -o, --output: Base name for output files. */
51 static const char *output_base = "render";
52
53 static const char *parse_options (int argc, char **argv);
54 static void usage (void) NO_RETURN;
55 static struct pivot_table *read_table (struct lexer *);
56 static void output_msg (const struct msg *, void *);
57
58 int
59 main (int argc, char **argv)
60 {
61   const char *input_file_name;
62
63   set_program_name (argv[0]);
64   i18n_init ();
65   output_engine_push ();
66   input_file_name = parse_options (argc, argv);
67
68   settings_init ();
69
70   struct lex_reader *reader = lex_reader_for_file (input_file_name, NULL,
71                                                    LEX_SYNTAX_AUTO,
72                                                    LEX_ERROR_CONTINUE);
73   if (!reader)
74     exit (1);
75
76   struct lexer *lexer = lex_create ();
77   msg_set_handler (output_msg, lexer);
78   lex_include (lexer, reader);
79   lex_get (lexer);
80
81   for (;;)
82     {
83       while (lex_match (lexer, T_ENDCMD))
84         continue;
85       if (lex_match (lexer, T_STOP))
86         break;
87
88       struct pivot_table *pt = read_table (lexer);
89       pivot_table_dump (pt, 0);
90       pivot_table_submit (pt);
91     }
92
93   lex_destroy (lexer);
94   output_engine_pop ();
95   fh_done ();
96
97   return 0;
98 }
99
100 static void PRINTF_FORMAT (2, 3)
101 register_driver (struct string_map *options,
102                  const char *output_file, ...)
103 {
104   va_list args;
105   va_start (args, output_file);
106   string_map_insert_nocopy (options, xstrdup ("output-file"),
107                             xvasprintf (output_file, args));
108   va_end (args);
109
110   struct output_driver *driver = output_driver_create (options);
111   if (driver == NULL)
112     exit (EXIT_FAILURE);
113   output_driver_register (driver);
114 }
115
116 static void
117 configure_drivers (int width, int length, int min_break)
118 {
119   /* Render to stdout. */
120   struct string_map options = STRING_MAP_INITIALIZER (options);
121   string_map_insert (&options, "format", "txt");
122   string_map_insert_nocopy (&options, xstrdup ("width"),
123                             xasprintf ("%d", width));
124   if (min_break >= 0)
125     string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
126                               xasprintf ("%d", min_break));
127   string_map_insert (&options, "emphasis", emphasis ? "true" : "false");
128   if (box != NULL)
129     string_map_insert (&options, "box", box);
130   register_driver (&options, "-");
131
132   /* Render to <base>.txt. */
133   register_driver (&options, "%s.txt", output_base);
134
135 #ifdef HAVE_CAIRO
136   /* Render to <base>.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   if (min_break >= 0)
144     {
145       string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
146                                 xasprintf ("%d", min_break * 5));
147       string_map_insert_nocopy (&options, xstrdup ("min-vbreak"),
148                                 xasprintf ("%d", min_break * 8));
149     }
150   register_driver (&options, "%s.pdf", output_base);
151 #endif
152
153   /* Render to <base>.csv. */
154   register_driver (&options, "%s.csv", output_base);
155
156   /* Render to <base>.odt. */
157   register_driver (&options, "%s.odt", output_base);
158
159   string_map_destroy (&options);
160 }
161
162 static const char *
163 parse_options (int argc, char **argv)
164 {
165   int width = 79;
166   int length = 66;
167   int min_break = -1;
168
169   for (;;)
170     {
171       enum {
172         OPT_WIDTH = UCHAR_MAX + 1,
173         OPT_LENGTH,
174         OPT_MIN_BREAK,
175         OPT_EMPHASIS,
176         OPT_BOX,
177         OPT_TABLE_LOOK,
178         OPT_HELP
179       };
180       static const struct option options[] =
181         {
182           {"width", required_argument, NULL, OPT_WIDTH},
183           {"length", required_argument, NULL, OPT_LENGTH},
184           {"min-break", required_argument, NULL, OPT_MIN_BREAK},
185           {"emphasis", no_argument, NULL, OPT_EMPHASIS},
186           {"box", required_argument, NULL, OPT_BOX},
187           {"output", required_argument, NULL, 'o'},
188           {"table-look", required_argument, NULL, OPT_TABLE_LOOK},
189           {"help", no_argument, NULL, OPT_HELP},
190           {NULL, 0, NULL, 0},
191         };
192
193       int c = getopt_long (argc, argv, "o:", options, NULL);
194       if (c == -1)
195         break;
196
197       switch (c)
198         {
199         case OPT_WIDTH:
200           width = atoi (optarg);
201           break;
202
203         case OPT_LENGTH:
204           length = atoi (optarg);
205           break;
206
207         case OPT_MIN_BREAK:
208           min_break = atoi (optarg);
209           break;
210
211         case OPT_EMPHASIS:
212           emphasis = true;
213           break;
214
215         case OPT_BOX:
216           box = optarg;
217           break;
218
219         case 'o':
220           output_base = optarg;
221           break;
222
223         case OPT_TABLE_LOOK:
224           {
225             struct pivot_table_look *look;
226             char *err = pivot_table_look_read (optarg, &look);
227             if (err)
228               error (1, 0, "%s", err);
229             pivot_table_look_set_default (look);
230             pivot_table_look_unref (look);
231           }
232           break;
233
234         case OPT_HELP:
235           usage ();
236
237         case 0:
238           break;
239
240         case '?':
241           exit(EXIT_FAILURE);
242           break;
243
244         default:
245           NOT_REACHED ();
246         }
247
248     }
249
250   configure_drivers (width, length, min_break);
251
252   if (optind + 1 != argc)
253     error (1, 0, "exactly one non-option argument required; "
254            "use --help for help");
255   return argv[optind];
256 }
257
258 static void
259 usage (void)
260 {
261   printf ("%s, to test rendering of PSPP tables\n"
262           "usage: %s [OPTIONS] INPUT\n"
263           "\nOptions:\n"
264           "  --width=WIDTH   set page width in characters\n"
265           "  --length=LINE   set page length in lines\n",
266           program_name, program_name);
267   exit (EXIT_SUCCESS);
268 }
269
270 static bool
271 parse_settings_value_show (struct lexer *lexer, const char *name,
272                            enum settings_value_show *show)
273 {
274   if (lex_match_id (lexer, name))
275     {
276       if (!lex_force_match (lexer, T_EQUALS))
277         exit (1);
278
279       if (lex_match_id (lexer, "DEFAULT"))
280         *show = SETTINGS_VALUE_SHOW_DEFAULT;
281       else if (lex_match_id (lexer, "VALUE"))
282         *show = SETTINGS_VALUE_SHOW_VALUE;
283       else if (lex_match_id (lexer, "LABEL"))
284         *show = SETTINGS_VALUE_SHOW_LABEL;
285       else if (lex_match_id (lexer, "BOTH"))
286         *show = SETTINGS_VALUE_SHOW_BOTH;
287       else
288         {
289           lex_error_expecting (lexer, "DEFAULT", "VALUE", "LABEL", "BOTH");
290           exit (1);
291         }
292
293       return true;
294     }
295   else
296     return false;
297 }
298
299 static bool
300 parse_string_setting (struct lexer *lexer, const char *name, char **stringp)
301 {
302   if (lex_match_id (lexer, name))
303     {
304       lex_match (lexer, T_EQUALS);
305       if (!lex_force_string (lexer))
306         exit (1);
307
308       free (*stringp);
309       *stringp = xstrdup (lex_tokcstr (lexer));
310
311       lex_get (lexer);
312       return true;
313     }
314   else
315     return false;
316 }
317
318 static void
319 read_value_option (struct lexer *lexer, struct pivot_value *value)
320 {
321   enum settings_value_show *show
322     = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.show
323        : value->type == PIVOT_VALUE_STRING ? &value->string.show
324        : value->type == PIVOT_VALUE_VARIABLE ? &value->variable.show
325        : NULL);
326   if (show && parse_settings_value_show (lexer, "SHOW", show))
327     return;
328
329   char **var_name
330     = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.var_name
331        : value->type == PIVOT_VALUE_STRING ? &value->string.var_name
332        : NULL);
333   if (var_name && parse_string_setting (lexer, "VAR", var_name))
334     return;
335
336   char **label
337     = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.value_label
338        : value->type == PIVOT_VALUE_STRING ? &value->string.value_label
339        : value->type == PIVOT_VALUE_VARIABLE ? &value->variable.var_label
340        : NULL);
341   if (label && parse_string_setting (lexer, "LABEL", label))
342     return;
343
344   if (value->type == PIVOT_VALUE_STRING && lex_match_id (lexer, "HEX"))
345     {
346       value->string.hex = true;
347       return;
348     }
349
350   if (value->type == PIVOT_VALUE_NUMERIC)
351     {
352       msg_disable ();
353       struct fmt_spec fmt;
354       bool ok = parse_format_specifier (lexer, &fmt);
355       msg_enable ();
356
357       if (ok)
358         {
359           if (!fmt_check_output (&fmt)
360               || !fmt_check_type_compat (&fmt, VAL_NUMERIC))
361             exit (1);
362
363           value->numeric.format = fmt;
364           return;
365         }
366     }
367
368   if (parse_string_setting (lexer, "SUPERSCRIPT", &value->superscript))
369     return;
370
371   if (lex_match_id (lexer, "SUBSCRIPTS"))
372     {
373       lex_match (lexer, T_EQUALS);
374       size_t allocated_subscripts = value->n_subscripts;
375       while (lex_token (lexer) == T_STRING)
376         {
377           if (value->n_subscripts >= allocated_subscripts)
378             value->subscripts = x2nrealloc (value->subscripts,
379                                             &allocated_subscripts,
380                                             sizeof *value->subscripts);
381
382           value->subscripts[value->n_subscripts++] = xstrdup (
383             lex_tokcstr (lexer));
384           lex_get (lexer);
385         }
386       return;
387     }
388
389   lex_error (lexer, "Expecting valid value option");
390   exit (1);
391 }
392
393 static struct pivot_value *
394 read_value (struct lexer *lexer)
395 {
396   struct pivot_value *value;
397   if (lex_is_number (lexer))
398     {
399       value = pivot_value_new_number (lex_number (lexer));
400       lex_get (lexer);
401     }
402   else if (lex_is_string (lexer))
403     {
404       value = xmalloc (sizeof *value);
405       *value = (struct pivot_value) {
406         .type = PIVOT_VALUE_STRING,
407         .string = { .s = xstrdup (lex_tokcstr (lexer)) },
408       };
409       lex_get (lexer);
410     }
411   else if (lex_token (lexer) == T_ID)
412     {
413       value = xmalloc (sizeof *value);
414       *value = (struct pivot_value) {
415         .type = PIVOT_VALUE_VARIABLE,
416         .variable = { .var_name = xstrdup (lex_tokcstr (lexer)) },
417       };
418       lex_get (lexer);
419     }
420   else
421     {
422       msg (SE, "Expecting pivot_value");
423       exit (1);
424     }
425
426   while (lex_match (lexer, T_LBRACK))
427     {
428       read_value_option (lexer, value);
429
430       if (!lex_force_match (lexer, T_RBRACK))
431         exit (1);
432     }
433
434   return value;
435 }
436
437 static void
438 read_group (struct lexer *lexer, struct pivot_table *pt,
439             struct pivot_category *group)
440 {
441   if (lex_match (lexer, T_ASTERISK))
442     group->show_label = true;
443
444   if (!lex_force_match (lexer, T_LPAREN))
445     exit (1);
446
447   if (lex_match (lexer, T_RPAREN))
448     return;
449
450   do
451     {
452       struct pivot_value *name = read_value (lexer);
453       if (lex_token (lexer) == T_ASTERISK
454           || lex_token (lexer) == T_LPAREN)
455         read_group (lexer, pt, pivot_category_create_group__ (group, name));
456       else
457         {
458           char *rc;
459           if (lex_token (lexer) == T_ID
460               && is_pivot_result_class (lex_tokcstr (lexer)))
461             {
462               rc = xstrdup (lex_tokcstr (lexer));
463               lex_get (lexer);
464             }
465           else
466             rc = NULL;
467
468           pivot_category_create_leaf_rc (group, name, rc);
469
470           free (rc);
471         }
472     }
473   while (lex_match (lexer, T_COMMA));
474   if (!lex_force_match (lexer, T_RPAREN))
475     exit (1);
476 }
477
478 static void
479 read_dimension (struct lexer *lexer, struct pivot_table *pt,
480                 enum pivot_axis_type a)
481 {
482   lex_match (lexer, T_EQUALS);
483
484   struct pivot_value *name = read_value (lexer);
485   struct pivot_dimension *dim = pivot_dimension_create__ (pt, a, name);
486   read_group (lexer, pt, dim->root);
487 }
488
489 static bool
490 parse_bool_setting (struct lexer *lexer, const char *name,
491                     const char *true_kw, const char *false_kw,
492                     bool *out)
493 {
494   if (lex_match_id (lexer, name))
495     {
496       if (!lex_force_match (lexer, T_EQUALS))
497         exit (1);
498
499       if (lex_match_id (lexer, true_kw))
500         *out = true;
501       else if (lex_match_id (lexer, false_kw))
502         *out = false;
503       else
504         {
505           lex_error_expecting (lexer, true_kw, false_kw);
506           exit (1);
507         }
508
509       return true;
510     }
511   else
512     return false;
513 }
514
515 static void
516 read_look (struct lexer *lexer, struct pivot_table *pt)
517 {
518   lex_match (lexer, T_EQUALS);
519
520   if (lex_is_string (lexer))
521     {
522       struct pivot_table_look *look;
523       char *error = pivot_table_look_read (lex_tokcstr (lexer), &look);
524       if (error)
525         {
526           msg (SE, "%s", error);
527           exit (1);
528         }
529       lex_get (lexer);
530
531       pivot_table_set_look (pt, look);
532       pivot_table_look_unref (look);
533     }
534
535   struct pivot_table_look *look = pivot_table_look_unshare (
536     pivot_table_look_ref (pt->look));
537   for (;;)
538     {
539       if (!parse_bool_setting (lexer, "EMPTY", "HIDE", "SHOW",
540                                &look->omit_empty)
541           && !parse_bool_setting (lexer, "ROWLABELS", "CORNER", "NESTED",
542                                   &look->row_labels_in_corner)
543           && !parse_bool_setting (lexer, "MARKERS", "NUMERIC", "ALPHA",
544                                   &look->show_numeric_markers)
545           && !parse_bool_setting (lexer, "LEVEL", "SUPER", "SUB",
546                                   &look->footnote_marker_superscripts)
547           && !parse_bool_setting (lexer, "LAYERS", "CURRENT", "ALL",
548                                   &look->print_all_layers)
549           && !parse_bool_setting (lexer, "PAGINATELAYERS", "YES", "NO",
550                                   &look->paginate_layers)
551           && !parse_bool_setting (lexer, "HSHRINK", "YES", "NO",
552                                   &look->shrink_to_fit[TABLE_HORZ])
553           && !parse_bool_setting (lexer, "VSHRINK", "YES", "NO",
554                                   &look->shrink_to_fit[TABLE_VERT])
555           && !parse_bool_setting (lexer, "TOPCONTINUATION", "YES", "NO",
556                                   &look->top_continuation)
557           && !parse_bool_setting (lexer, "BOTTOMCONTINUATION", "YES", "NO",
558                                   &look->bottom_continuation)
559           && !parse_string_setting (lexer, "CONTINUATION",
560                                     &look->continuation))
561         break;
562     }
563   pivot_table_set_look (pt, look);
564   pivot_table_look_unref (look);
565 }
566
567 static enum table_stroke
568 read_stroke (struct lexer *lexer)
569 {
570   for (int stroke = 0; stroke < TABLE_N_STROKES; stroke++)
571     if (lex_match_id (lexer, table_stroke_to_string (stroke)))
572       return stroke;
573
574   lex_error (lexer, "expecting stroke");
575   exit (1);
576 }
577
578 static bool
579 parse_value_setting (struct lexer *lexer, const char *name,
580                      struct pivot_value **valuep)
581 {
582   if (lex_match_id (lexer, name))
583     {
584       lex_match (lexer, T_EQUALS);
585
586       pivot_value_destroy (*valuep);
587       *valuep = read_value (lexer);
588
589       return true;
590     }
591   else
592     return false;
593 }
594
595 static void
596 read_border (struct lexer *lexer, struct pivot_table *pt)
597 {
598   static const char *const pivot_border_ids[PIVOT_N_BORDERS] = {
599     [PIVOT_BORDER_TITLE] = "title",
600     [PIVOT_BORDER_OUTER_LEFT] = "outer-left",
601     [PIVOT_BORDER_OUTER_TOP] = "outer-top",
602     [PIVOT_BORDER_OUTER_RIGHT] = "outer-right",
603     [PIVOT_BORDER_OUTER_BOTTOM] = "outer-bottom",
604     [PIVOT_BORDER_INNER_LEFT] = "inner-left",
605     [PIVOT_BORDER_INNER_TOP] = "inner-top",
606     [PIVOT_BORDER_INNER_RIGHT] = "inner-right",
607     [PIVOT_BORDER_INNER_BOTTOM] = "inner-bottom",
608     [PIVOT_BORDER_DATA_LEFT] = "data-left",
609     [PIVOT_BORDER_DATA_TOP] = "data-top",
610     [PIVOT_BORDER_DIM_ROW_HORZ] = "dim-row-horz",
611     [PIVOT_BORDER_DIM_ROW_VERT] = "dim-row-vert",
612     [PIVOT_BORDER_DIM_COL_HORZ] = "dim-col-horz",
613     [PIVOT_BORDER_DIM_COL_VERT] = "dim-col-vert",
614     [PIVOT_BORDER_CAT_ROW_HORZ] = "cat-row-horz",
615     [PIVOT_BORDER_CAT_ROW_VERT] = "cat-row-vert",
616     [PIVOT_BORDER_CAT_COL_HORZ] = "cat-col-horz",
617     [PIVOT_BORDER_CAT_COL_VERT] = "cat-col-vert",
618   };
619
620   lex_match (lexer, T_EQUALS);
621
622   struct pivot_table_look *look = pivot_table_look_unshare (
623     pivot_table_look_ref (pt->look));
624   while (lex_token (lexer) == T_STRING)
625     {
626       char *s = xstrdup (lex_tokcstr (lexer));
627       lex_get (lexer);
628       if (!lex_force_match (lexer, T_LPAREN))
629         exit (1);
630
631       struct table_border_style style = TABLE_BORDER_STYLE_INITIALIZER;
632       style.stroke = read_stroke (lexer);
633       if (lex_is_string (lexer))
634         {
635           if (!parse_color__ (lex_tokcstr (lexer), &style.color))
636             {
637               msg (SE, "%s: unknown color", lex_tokcstr (lexer));
638               exit (1);
639             }
640           lex_get (lexer);
641         }
642       if (!lex_force_match (lexer, T_RPAREN))
643         exit (1);
644
645       int n = 0;
646       for (int b = 0; b < PIVOT_N_BORDERS; b++)
647         {
648           if (!strncmp (s, pivot_border_ids[b], strlen (s)))
649             {
650               look->borders[b] = style;
651               n++;
652             }
653         }
654       if (!n)
655         {
656           msg (SE, "%s: no matching borders", s);
657           exit (1);
658         }
659       free (s);
660     }
661   pivot_table_set_look (pt, look);
662   pivot_table_look_unref (look);
663 }
664
665 static struct pivot_table *
666 read_table (struct lexer *lexer)
667 {
668   struct pivot_table *pt = pivot_table_create__ (NULL, NULL);
669   while (lex_match (lexer, T_SLASH))
670     {
671       assert (!pivot_table_is_shared (pt));
672
673       if (lex_match_id (lexer, "ROW"))
674         read_dimension (lexer, pt, PIVOT_AXIS_ROW);
675       else if (lex_match_id (lexer, "COLUMN"))
676         read_dimension (lexer, pt, PIVOT_AXIS_COLUMN);
677       else if (lex_match_id (lexer, "LAYER"))
678         read_dimension (lexer, pt, PIVOT_AXIS_LAYER);
679       else if (lex_match_id (lexer, "LOOK"))
680         read_look (lexer, pt);
681       else if (lex_match_id (lexer, "ROTATE"))
682         {
683           lex_match (lexer, T_EQUALS);
684           while (lex_token (lexer) == T_ID)
685             if (!parse_bool_setting (lexer, "INNERCOLUMNS", "YES", "NO",
686                                      &pt->rotate_inner_column_labels)
687                 && !parse_bool_setting (lexer, "OUTERROWS", "YES", "NO",
688                                         &pt->rotate_outer_row_labels))
689               break;
690         }
691       else if (lex_match_id (lexer, "DISPLAY"))
692         {
693           lex_match (lexer, T_EQUALS);
694           while (lex_token (lexer) == T_ID)
695             {
696               if (parse_bool_setting (lexer, "GRID", "YES", "NO",
697                                       &pt->show_grid_lines)
698                   || parse_bool_setting (lexer, "CAPTION", "YES", "NO",
699                                          &pt->show_caption)
700                   || parse_bool_setting (lexer, "TITLE", "YES", "NO",
701                                          &pt->show_title))
702                 continue;
703
704               if (parse_settings_value_show (lexer, "VALUES", &pt->show_values)
705                   || parse_settings_value_show (lexer, "VARIABLES",
706                                                 &pt->show_variables))
707                 continue;
708
709               break;
710             }
711         }
712       else if (parse_value_setting (lexer, "TITLE", &pt->title)
713                || parse_value_setting (lexer, "SUBTYPE", &pt->subtype)
714                || parse_value_setting (lexer, "CORNER", &pt->corner_text)
715                || parse_value_setting (lexer, "CAPTION", &pt->caption)
716                || parse_string_setting (lexer, "NOTES", &pt->notes))
717         {
718           /* Nothing. */
719         }
720       else if (lex_match_id (lexer, "BORDER"))
721         read_border (lexer, pt);
722       else
723         {
724           msg (SE, "Expecting keyword");
725           exit (1);
726         }
727     }
728
729   if (!lex_force_match (lexer, T_ENDCMD))
730     exit (1);
731   return pt;
732 }
733
734 static void
735 output_msg (const struct msg *m_, void *lexer_)
736 {
737   struct lexer *lexer = lexer_;
738   struct msg m = *m_;
739
740   if (m.file_name == NULL)
741     {
742       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
743       m.first_line = lex_get_first_line_number (lexer, 0);
744       m.last_line = lex_get_last_line_number (lexer, 0);
745     }
746
747   m.command_name = output_get_command_name ();
748
749   message_item_submit (message_item_create (&m));
750
751   free (m.command_name);
752 }