c1d246360f3b9b7b036c812c7c28de5b989386d8
[pspp] / src / output / journal.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2010, 2012 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 "output/journal.h"
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include "data/file-name.h"
26 #include "libpspp/cast.h"
27 #include "libpspp/message.h"
28 #include "libpspp/str.h"
29 #include "output/driver-provider.h"
30 #include "output/message-item.h"
31 #include "output/text-item.h"
32
33 #include "gl/fwriteerror.h"
34 #include "gl/xalloc.h"
35
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
38
39 struct journal_driver
40   {
41     struct output_driver driver;
42     FILE *file;
43     char *command_name;
44
45     /* Name of journal file. */
46     char *file_name;
47   };
48
49 static const struct output_driver_class journal_class;
50
51 /* Journal driver, if journaling is enabled. */
52 static struct journal_driver *journal;
53
54
55 static struct journal_driver *
56 journal_driver_cast (struct output_driver *driver)
57 {
58   assert (driver->class == &journal_class);
59   return UP_CAST (driver, struct journal_driver, driver);
60 }
61
62 static void
63 journal_close (void)
64 {
65   if (journal != NULL && journal->file != NULL)
66     {
67       if (fwriteerror (journal->file))
68         msg_error (errno, _("error writing output file `%s'"),
69                journal->file_name);
70       journal->file = NULL;
71     }
72 }
73
74 static void
75 journal_destroy (struct output_driver *driver)
76 {
77   struct journal_driver *j = journal_driver_cast (driver);
78
79   journal_close ();
80   free (j->command_name);
81   free (j);
82
83   journal = NULL;
84 }
85
86 static void
87 journal_output (struct journal_driver *j, const char *s)
88 {
89   if (j->file == NULL)
90     {
91       j->file = fopen (journal_get_file_name (), "a");
92       if (j->file == NULL)
93         {
94           msg_error (errno, _("error opening output file `%s'"),
95                  journal_get_file_name ());
96           output_driver_destroy (&j->driver);
97           return;
98         }
99     }
100
101   fprintf (j->file, "%s\n", s);
102
103   /* Flush the journal in case the syntax we're about to write
104      causes a crash.  Having the syntax already written to disk
105      makes postmortem analysis of the problem possible. */
106   fflush (j->file);
107 }
108
109 static void
110 journal_submit (struct output_driver *driver, const struct output_item *item)
111 {
112   struct journal_driver *j = journal_driver_cast (driver);
113
114   output_driver_track_current_command (item, &j->command_name);
115
116   if (is_text_item (item))
117     {
118       const struct text_item *text_item = to_text_item (item);
119       enum text_item_type type = text_item_get_type (text_item);
120
121       if (type == TEXT_ITEM_SYNTAX)
122         journal_output (j, text_item_get_text (text_item));
123     }
124   else if (is_message_item (item))
125     {
126       const struct message_item *message_item = to_message_item (item);
127       const struct msg *msg = message_item_get_msg (message_item);
128       char *s = msg_to_string (msg, j->command_name);
129       journal_output (j, s);
130       free (s);
131     }
132 }
133
134 static const struct output_driver_class journal_class =
135   {
136     "journal",
137     journal_destroy,
138     journal_submit,
139     NULL                        /* flush */
140   };
141 \f
142 /* Enables journaling. */
143 void
144 journal_enable (void)
145 {
146   if (journal == NULL)
147     {
148       /* Create journal driver. */
149       journal = xzalloc (sizeof *journal);
150       output_driver_init (&journal->driver, &journal_class, "journal",
151                           SETTINGS_DEVICE_UNFILTERED);
152       journal->file = NULL;
153       journal->command_name = NULL;
154
155       /* Register journal driver. */
156       output_driver_register (&journal->driver);
157     }
158 }
159
160 /* Disables journaling. */
161 void
162 journal_disable (void)
163 {
164   if (journal != NULL)
165     output_driver_destroy (&journal->driver);
166 }
167
168 /* Returns true if journaling is enabled, false otherwise. */
169 bool
170 journal_is_enabled (void)
171 {
172   return journal != NULL;
173 }
174
175 /* Sets the name of the journal file to FILE_NAME. */
176 void
177 journal_set_file_name (const char *file_name)
178 {
179   journal_close ();
180   free (journal->file_name);
181   journal->file_name = xstrdup (file_name);
182 }
183
184 /* Returns the name of the journal file.  The caller must not modify or free
185    the returned string. */
186 const char *
187 journal_get_file_name (void)
188 {
189   if (journal->file_name == NULL)
190     {
191       const char *output_path = default_output_path ();
192       journal->file_name = xasprintf ("%s%s", output_path, "pspp.jnl");
193     }
194   return journal->file_name;
195 }