This allows groups to be nested.
#include "libpspp/i18n.h"
#include "libpspp/message.h"
#include "libpspp/str.h"
-#include "output/text-item.h"
+#include "output/group-item.h"
#include "xalloc.h"
#include "xmalloca.h"
result = CMD_FAILURE;
goto finish;
}
- text_item_submit (text_item_create (TEXT_ITEM_COMMAND_OPEN, command->name));
+ group_open_item_submit (group_open_item_create (command->name));
opened = true;
if (command->function == NULL)
lex_get (lexer);
if (opened)
- text_item_submit (text_item_create (TEXT_ITEM_COMMAND_CLOSE,
- command->name));
+ group_close_item_submit (group_close_item_create ());
return result;
}
new_msg = xmemdup (m, sizeof *m);
if (m->file_name != NULL)
new_msg->file_name = xstrdup (m->file_name);
+ if (m->command_name != NULL)
+ new_msg->command_name = xstrdup (m->command_name);
new_msg->text = xstrdup (m->text);
return new_msg;
{
free (m->file_name);
free (m->text);
+ free (m->command_name);
free (m);
}
char *
-msg_to_string (const struct msg *m, const char *command_name)
+msg_to_string (const struct msg *m)
{
struct string s;
ds_put_format (&s, "%s: ", msg_severity_to_string (m->severity));
- if (m->category == MSG_C_SYNTAX && command_name != NULL)
- ds_put_format (&s, "%s: ", command_name);
+ if (m->category == MSG_C_SYNTAX && m->command_name != NULL)
+ ds_put_format (&s, "%s: ", m->command_name);
ds_put_cstr (&s, m->text);
process_msg (m);
free (m->text);
+ free (m->command_name);
}
/* Disables message output until the next call to msg_enable. If
enum msg_category category; /* Message category. */
enum msg_severity severity; /* Message severity. */
char *file_name; /* Name of file containing error, or NULL. */
+ char *command_name; /* Name of erroneous command, or NULL. */
int first_line; /* 1-based line number, or 0 if none. */
int last_line; /* 1-based exclusive last line (0=none). */
int first_column; /* 1-based first column, or 0 if none. */
/* Working with messages. */
struct msg *msg_dup (const struct msg *);
void msg_destroy(struct msg *);
-char *msg_to_string (const struct msg *, const char *command_name);
+char *msg_to_string (const struct msg *);
/* Emitting messages. */
void vmsg (enum msg_class class, const char *format, va_list args)
switch (type)
{
case TEXT_ITEM_PAGE_TITLE:
- case TEXT_ITEM_COMMAND_OPEN:
- case TEXT_ITEM_COMMAND_CLOSE:
- break;
-
case TEXT_ITEM_BLANK_LINE:
break;
else if (is_message_item (output_item))
{
const struct message_item *message_item = to_message_item (output_item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, message_item->command_name);
+ char *s = msg_to_string (message_item_get_msg (message_item));
ascii_output_text (a, s);
free (s);
}
src/output/driver-provider.h \
src/output/driver.c \
src/output/driver.h \
+ src/output/group-item.c \
+ src/output/group-item.h \
src/output/html.c \
src/output/journal.c \
src/output/journal.h \
else if (is_message_item (item))
{
const struct message_item *message_item = to_message_item (item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, NULL);
+ char *s = msg_to_string (message_item_get_msg (message_item));
r = xr_rendering_create_text (xr, s, cr);
free (s);
}
case TEXT_ITEM_PAGE_TITLE:
break;
- case TEXT_ITEM_COMMAND_CLOSE:
- break;
-
case TEXT_ITEM_BLANK_LINE:
if (xr->y > 0)
xr->y += xr->char_height;
xr_render_message (struct xr_driver *xr,
const struct message_item *message_item)
{
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, message_item->command_name);
+ char *s = msg_to_string (message_item_get_msg (message_item));
struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
free (s);
struct xr_render_fsm *fsm = xr_create_text_renderer (xr, item);
enum text_item_type type = text_item_get_type (text_item);
const char *text = text_item_get_text (text_item);
- if (type == TEXT_ITEM_COMMAND_OPEN || type == TEXT_ITEM_COMMAND_CLOSE
- || type == TEXT_ITEM_SYNTAX || type == TEXT_ITEM_PAGE_TITLE)
+ if (type == TEXT_ITEM_SYNTAX || type == TEXT_ITEM_PAGE_TITLE)
return;
csv_put_separator (csv);
else if (is_message_item (output_item))
{
const struct message_item *message_item = to_message_item (output_item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, message_item->command_name);
+ char *s = msg_to_string (message_item_get_msg (message_item));
csv_put_separator (csv);
csv_output_field (csv, s);
free (s);
#include "libpspp/string-map.h"
#include "libpspp/string-set.h"
#include "libpspp/str.h"
+#include "output/group-item.h"
#include "output/message-item.h"
#include "output/output-item.h"
#include "output/text-item.h"
struct string deferred_syntax; /* TEXT_ITEM_SYNTAX being accumulated. */
char *command_name; /* Name of command being processed. */
char *title, *subtitle; /* Components of page title. */
+
+ /* Output grouping stack.
+
+ TEXT_ITEM_GROUP_OPEN pushes a group on the stack and
+ TEXT_ITEM_GROUP_CLOSE pops one off. */
+ char **groups; /* Command names of nested sections. */
+ size_t n_groups;
+ size_t allocated_groups;
};
static const struct output_driver_factory *factories[];
sizeof *engine_stack);
e = &engine_stack[n_stack++];
+ memset (e, 0, sizeof *e);
llx_init (&e->drivers);
ds_init_empty (&e->deferred_syntax);
- e->command_name = NULL;
e->title = NULL;
e->subtitle = NULL;
}
free (e->command_name);
free (e->title);
free (e->subtitle);
+ for (size_t i = 0; i < e->n_groups; i++)
+ free (e->groups[i]);
+ free (e->groups);
}
void
flush_deferred_syntax (e);
- if (is_text_item (item))
+ if (is_group_open_item (item))
{
- const struct text_item *text_item = to_text_item (item);
- const char *text = text_item_get_text (text_item);
- enum text_item_type type = text_item_get_type (text_item);
+ const struct group_open_item *group_open_item
+ = to_group_open_item (item);
+ if (e->n_groups >= e->allocated_groups)
+ e->groups = x2nrealloc (e->groups, &e->allocated_groups,
+ sizeof *e->groups);
+ e->groups[e->n_groups] = (group_open_item->command_name
+ ? xstrdup (group_open_item->command_name)
+ : NULL);
+ e->n_groups++;
+ }
+ else if (is_group_close_item (item))
+ {
+ assert (e->n_groups > 0);
- if (type == TEXT_ITEM_COMMAND_OPEN)
- {
- free (e->command_name);
- e->command_name = xstrdup (text);
- }
- else if (type == TEXT_ITEM_COMMAND_CLOSE)
- {
- free (e->command_name);
- e->command_name = NULL;
- }
+ size_t idx = --e->n_groups;
+ free (e->groups[idx]);
}
- else if (is_message_item (item))
+ output_submit__ (e, item);
+}
+
+const char *
+output_get_command_name (void)
+{
+ if (n_stack)
{
- struct message_item *message_item = to_message_item (item);
- free (message_item->command_name);
- message_item->command_name = (e->command_name
- ? xstrdup (e->command_name)
- : NULL);
+ struct output_engine *e = engine_stack_top ();
+ for (size_t i = e->n_groups; i-- > 0; )
+ if (e->groups[i])
+ return e->groups[i];
}
- output_submit__ (e, item);
+ return NULL;
}
/* Flushes output to screen devices, so that the user can see
output_set_title__ (e, &e->subtitle, subtitle);
}
+
+size_t
+output_get_group_level (void)
+{
+ struct output_engine *e = engine_stack_top ();
+
+ return e->n_groups;
+}
\f
void
output_driver_init (struct output_driver *driver,
#define OUTPUT_DRIVER_H 1
#include <stdbool.h>
+#include <stddef.h>
struct output_item;
struct string_set;
void output_set_title (const char *);
void output_set_subtitle (const char *);
+const char *output_get_command_name (void);
+
+size_t output_get_group_level (void);
void output_driver_parse_option (const char *option,
struct string_map *options);
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2018 Free Sonftware Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "output/group-item.h"
+
+#include <stdlib.h>
+
+#include "output/driver.h"
+#include "output/output-item-provider.h"
+
+#include "gl/xalloc.h"
+
+struct group_open_item *
+group_open_item_create (const char *command_name)
+{
+ struct group_open_item *item;
+
+ item = xmalloc (sizeof *item);
+ output_item_init (&item->output_item, &group_open_item_class);
+ item->command_name = command_name ? xstrdup (command_name) : NULL;
+
+ return item;
+}
+
+/* Submits ITEM to the configured output drivers, and transfers ownership to
+ the output subsystem. */
+void
+group_open_item_submit (struct group_open_item *item)
+{
+ output_submit (&item->output_item);
+}
+
+static void
+group_open_item_destroy (struct output_item *output_item)
+{
+ struct group_open_item *item = to_group_open_item (output_item);
+
+ free (item->command_name);
+ free (item);
+}
+
+const struct output_item_class group_open_item_class =
+ {
+ group_open_item_destroy,
+ };
+\f
+struct group_close_item *
+group_close_item_create (void)
+{
+ struct group_close_item *item;
+
+ item = xmalloc (sizeof *item);
+ output_item_init (&item->output_item, &group_close_item_class);
+
+ return item;
+}
+
+/* Submits ITEM to the configured output drivers, and transfers ownership to
+ the output subsystem. */
+void
+group_close_item_submit (struct group_close_item *item)
+{
+ output_submit (&item->output_item);
+}
+
+static void
+group_close_item_destroy (struct output_item *output_item)
+{
+ struct group_close_item *item = to_group_close_item (output_item);
+
+ free (item);
+}
+
+const struct output_item_class group_close_item_class =
+ {
+ group_close_item_destroy,
+ };
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2018 Free Sonftware Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_GROUP_ITEM_H
+#define OUTPUT_GROUP_ITEM_H 1
+
+/* Grouping items.
+
+ A group-open item marks the beginning of a group of related output items. A
+ later group-close item ends the group. Groups can nest. */
+
+#include <stdbool.h>
+#include "output/output-item.h"
+
+/* A group_open item. */
+struct group_open_item
+ {
+ struct output_item output_item;
+ char *command_name;
+ };
+
+struct group_open_item *group_open_item_create (const char *command_name);
+
+/* A group_close item. */
+struct group_close_item
+ {
+ struct output_item output_item;
+ };
+struct group_close_item *group_close_item_create (void);
+\f
+/* This boilerplate for group_open_item, a subclass of output_item, was
+ autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include "libpspp/cast.h"
+
+extern const struct output_item_class group_open_item_class;
+
+/* Returns true if SUPER is a group_open_item, otherwise false. */
+static inline bool
+is_group_open_item (const struct output_item *super)
+{
+ return super->class == &group_open_item_class;
+}
+
+/* Returns SUPER converted to group_open_item. SUPER must be a
+ group_open_item, as reported by is_group_open_item. */
+static inline struct group_open_item *
+to_group_open_item (const struct output_item *super)
+{
+ assert (is_group_open_item (super));
+ return UP_CAST (super, struct group_open_item, output_item);
+}
+
+/* Returns INSTANCE converted to output_item. */
+static inline struct output_item *
+group_open_item_super (const struct group_open_item *instance)
+{
+ return CONST_CAST (struct output_item *, &instance->output_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct group_open_item *
+group_open_item_ref (const struct group_open_item *instance)
+{
+ return to_group_open_item (output_item_ref (&instance->output_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+ the reference count is now zero. */
+static inline void
+group_open_item_unref (struct group_open_item *instance)
+{
+ output_item_unref (&instance->output_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+ false otherwise. */
+static inline bool
+group_open_item_is_shared (const struct group_open_item *instance)
+{
+ return output_item_is_shared (&instance->output_item);
+}
+
+void group_open_item_submit (struct group_open_item *);
+\f
+/* This boilerplate for group_close_item, a subclass of output_item, was
+ autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include "libpspp/cast.h"
+
+extern const struct output_item_class group_close_item_class;
+
+/* Returns true if SUPER is a group_close_item, otherwise false. */
+static inline bool
+is_group_close_item (const struct output_item *super)
+{
+ return super->class == &group_close_item_class;
+}
+
+/* Returns SUPER converted to group_close_item. SUPER must be a
+ group_close_item, as reported by is_group_close_item. */
+static inline struct group_close_item *
+to_group_close_item (const struct output_item *super)
+{
+ assert (is_group_close_item (super));
+ return UP_CAST (super, struct group_close_item, output_item);
+}
+
+/* Returns INSTANCE converted to output_item. */
+static inline struct output_item *
+group_close_item_super (const struct group_close_item *instance)
+{
+ return CONST_CAST (struct output_item *, &instance->output_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct group_close_item *
+group_close_item_ref (const struct group_close_item *instance)
+{
+ return to_group_close_item (output_item_ref (&instance->output_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+ the reference count is now zero. */
+static inline void
+group_close_item_unref (struct group_close_item *instance)
+{
+ output_item_unref (&instance->output_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+ false otherwise. */
+static inline bool
+group_close_item_is_shared (const struct group_close_item *instance)
+{
+ return output_item_is_shared (&instance->output_item);
+}
+
+void group_close_item_submit (struct group_close_item *);
+\f
+#endif /* output/group-item.h */
case TEXT_ITEM_PAGE_TITLE:
break;
- case TEXT_ITEM_COMMAND_OPEN:
- fprintf (html->file, "<DIV class=\"");
- escape_string (html->file, s, strlen (s), "_", "<BR>");
- fprintf (html->file, "\">");
- print_title_tag (html->file, "H3", s);
- break;
-
- case TEXT_ITEM_COMMAND_CLOSE:
- fprintf (html->file, "</DIV>\n");
- break;
-
case TEXT_ITEM_SUBHEAD:
print_title_tag (html->file, "H4", s);
break;
else if (is_message_item (output_item))
{
const struct message_item *message_item = to_message_item (output_item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, message_item->command_name);
+ char *s = msg_to_string (message_item_get_msg (message_item));
print_title_tag (html->file, "P", s);
free (s);
}
else if (is_message_item (item))
{
const struct message_item *message_item = to_message_item (item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, message_item->command_name);
+ char *s = msg_to_string (message_item_get_msg (message_item));
journal_output (j, s);
free (s);
}
item = xmalloc (sizeof *msg);
output_item_init (&item->output_item, &message_item_class);
item->msg = msg_dup (msg);
- item->command_name = NULL;
return item;
}
{
struct message_item *item = to_message_item (output_item);
msg_destroy (item->msg);
- free (item->command_name);
free (item);
}
{
struct output_item output_item;
struct msg *msg;
- char *command_name;
};
struct message_item *message_item_create (const struct msg *);
if (is_message_item (item))
{
const struct message_item *message_item = to_message_item (item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, message_item->command_name);
+ char *s = msg_to_string (message_item_get_msg (message_item));
fprintf (ml->file, "%s\n", s);
free (s);
}
if (is_table_item (output_item))
write_table (odt, to_table_item (output_item));
else if (is_text_item (output_item))
- {
- struct text_item *text_item = to_text_item (output_item);
-
- if (text_item_get_type (text_item) != TEXT_ITEM_COMMAND_CLOSE)
- odt_output_text (odt, text_item_get_text (text_item));
- }
+ odt_output_text (odt, text_item_get_text (to_text_item (output_item)));
else if (is_message_item (output_item))
{
const struct message_item *message_item = to_message_item (output_item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, message_item->command_name);
+ char *s = msg_to_string (message_item_get_msg (message_item));
odt_output_text (odt, s);
free (s);
}
enum text_item_type
{
- /* Each PSPP command is bracketed between a pair of these text items. The
- text item's string is the full name of the command. The syntax text
- items associated with the command, as well as all output produced
- directly by the command, are contained within the pair. There is no
- nesting. */
- TEXT_ITEM_COMMAND_OPEN, /* Command starting. */
- TEXT_ITEM_COMMAND_CLOSE, /* Command completed. */
-
/* Headings. */
TEXT_ITEM_PAGE_TITLE, /* TITLE and SUBTITLE commands. */
TEXT_ITEM_SUBHEAD, /* Heading within a command's output.*/
#include "output/driver-provider.h"
#include "output/driver.h"
#include "output/chart-item.h"
+#include "output/group-item.h"
#include "output/message-item.h"
#include "output/output-item.h"
#include "output/table-item.h"
GtkTreeIter iter;
int tw, th;
- if (is_text_item (item))
+ if (is_group_close_item (item))
{
- const struct text_item *text_item = to_text_item (item);
- enum text_item_type type = text_item_get_type (text_item);
- const char *text = text_item_get_text (text_item);
-
- if (type == TEXT_ITEM_COMMAND_CLOSE)
+ if (output_get_group_level () == 0)
{
view->in_command = false;
return;
}
- else if (text[0] == '\0')
+ }
+ else if (is_text_item (item))
+ {
+ const struct text_item *text_item = to_text_item (item);
+ const char *text = text_item_get_text (text_item);
+ if (text[0] == '\0')
return;
}
store = GTK_TREE_STORE (gtk_tree_view_get_model (view->overview));
ds_init_empty (&name);
- if (is_text_item (item)
- && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
+ if (is_group_open_item (item) && output_get_group_level () == 1)
{
gtk_tree_store_append (store, &iter, NULL);
view->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
m.first_column = lex_get_first_column (lexer, 0);
m.last_column = lex_get_last_column (lexer, 0);
}
+ m.command_name = CONST_CAST (char *, output_get_command_name ());
message_item_submit (message_item_create (&m));
}
m.last_line = lex_get_last_line_number (lexer, 0);
}
+ m.command_name = CONST_CAST (char *, output_get_command_name ());
+
message_item_submit (message_item_create (&m));
}
EXECUTE.
])
AT_CHECK([pspp -O format=csv input-program.sps], [1], [dnl
-input-program.sps:3: error: Input program must contain DATA LIST or END FILE.
+input-program.sps:3: error: INPUT PROGRAM: Input program must contain DATA LIST or END FILE.
input-program.sps:4: error: EXECUTE: EXECUTE is allowed only after the active dataset has been defined.
])