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