File: | cafe-screenshot/src/screenshot-save.c |
Warning: | line 223, column 15 This statement is never executed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* screenshot-save.c - image saving functions for CAFE Screenshot |
2 | * |
3 | * Copyright (C) 2001-2006 Jonathan Blandford <jrb@alum.mit.edu> |
4 | * |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public |
16 | * License along with this program; if not, write to the |
17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
18 | */ |
19 | |
20 | #include <config.h> |
21 | #include <stdlib.h> |
22 | #include <unistd.h> |
23 | #include <errno(*__errno_location ()).h> |
24 | #include <signal.h> |
25 | #include <string.h> |
26 | #include <glib/gi18n.h> |
27 | |
28 | #include "screenshot-save.h" |
29 | |
30 | static char *parent_dir = NULL((void*)0); |
31 | static char *tmp_filename = NULL((void*)0); |
32 | |
33 | static SaveFunction save_callback = NULL((void*)0); |
34 | static gpointer save_user_data = NULL((void*)0); |
35 | |
36 | /* Strategy for saving: |
37 | * |
38 | * We keep another process around to handle saving the image. This is |
39 | * done for two reasons. One, the saving takes a non-zero amount of |
40 | * time (about a quarter of a second on my box.) This will make it |
41 | * more interactive. The second reason is to make the child |
42 | * responsible for cleaning up the temp dir. If the parent process is |
43 | * killed or segfaults, the child process can clean up the temp dir. |
44 | */ |
45 | static void |
46 | clean_up_temporary_dir (gboolean gui_on_error) |
47 | { |
48 | char *message; |
49 | gboolean error_occurred = FALSE(0); |
50 | if (g_file_test (tmp_filename, G_FILE_TEST_EXISTS)) |
51 | error_occurred = unlink (tmp_filename); |
52 | if (g_file_test (parent_dir, G_FILE_TEST_EXISTS)) |
53 | error_occurred = rmdir (parent_dir) || error_occurred; |
54 | |
55 | if (error_occurred) |
56 | { |
57 | message = g_strdup_printf (_("Unable to clear the temporary folder:\n%s")gettext ("Unable to clear the temporary folder:\n%s"), |
58 | tmp_filename); |
59 | if (gui_on_error) |
60 | { |
61 | CtkWidget *dialog; |
62 | |
63 | dialog = ctk_message_dialog_new (NULL((void*)0), 0, |
64 | CTK_MESSAGE_ERROR, |
65 | CTK_BUTTONS_OK, |
66 | "%s", message); |
67 | ctk_dialog_run (CTK_DIALOG (dialog)((((CtkDialog*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((dialog)), ((ctk_dialog_get_type ()))))))); |
68 | ctk_widget_destroy (dialog); |
69 | } |
70 | else |
71 | { |
72 | g_warning ("%s", message); |
73 | } |
74 | g_free (message); |
75 | } |
76 | g_free (tmp_filename); |
77 | g_free (parent_dir); |
78 | } |
79 | |
80 | static void |
81 | child_done_notification (GPid pid, |
82 | gint status, |
83 | gpointer data) |
84 | { |
85 | /* This should never be called. */ |
86 | |
87 | /* We expect the child to die after the parent. If the child dies |
88 | * than it either segfaulted, or was randomly killed. In either |
89 | * case, we can't reasonably continue. */ |
90 | CtkWidget *dialog; |
91 | |
92 | dialog = ctk_message_dialog_new (NULL((void*)0), 0, |
93 | CTK_MESSAGE_ERROR, |
94 | CTK_BUTTONS_OK, |
95 | _("The child save process unexpectedly exited. We are unable to write the screenshot to disk.")gettext ("The child save process unexpectedly exited. We are unable to write the screenshot to disk." )); |
96 | ctk_dialog_run (CTK_DIALOG (dialog)((((CtkDialog*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((dialog)), ((ctk_dialog_get_type ()))))))); |
97 | ctk_widget_destroy (dialog); |
98 | |
99 | clean_up_temporary_dir (TRUE(!(0))); |
100 | |
101 | exit (1); |
102 | } |
103 | |
104 | static gboolean |
105 | read_pipe_from_child (GIOChannel *source, |
106 | GIOCondition condition, |
107 | gpointer data) |
108 | { |
109 | if (condition & G_IO_IN) |
110 | { |
111 | gchar *message = NULL((void*)0); |
112 | gchar *error_message = NULL((void*)0); |
113 | CtkWidget *dialog; |
114 | GIOStatus status; |
115 | |
116 | status = g_io_channel_read_line (source, &error_message, NULL((void*)0), NULL((void*)0), NULL((void*)0)); |
117 | |
118 | if (status == G_IO_STATUS_NORMAL) |
119 | { |
120 | message = g_strdup_printf ("Unable to save the screenshot to disk:\n\n%s", error_message); |
121 | dialog = ctk_message_dialog_new (NULL((void*)0), 0, |
122 | CTK_MESSAGE_ERROR, |
123 | CTK_BUTTONS_OK, |
124 | "%s", message); |
125 | ctk_dialog_run (CTK_DIALOG (dialog)((((CtkDialog*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((dialog)), ((ctk_dialog_get_type ()))))))); |
126 | ctk_widget_destroy (dialog); |
127 | exit (1); |
128 | } |
129 | } |
130 | |
131 | (*save_callback) (save_user_data); |
132 | |
133 | return FALSE(0); |
134 | } |
135 | |
136 | static char * |
137 | make_temp_directory (void) |
138 | { |
139 | gint result, i; |
140 | gchar *dir_name; |
141 | |
142 | i = 0; |
143 | do |
144 | { |
145 | gchar *tmp_dir = g_strdup_printf ("cafe-screenshot.%u.%d", |
146 | (unsigned int) getpid (), |
147 | i++); |
148 | |
149 | dir_name = g_build_filename (g_get_tmp_dir (), |
150 | tmp_dir, |
151 | NULL((void*)0)); |
152 | g_free (tmp_dir); |
153 | |
154 | result = g_mkdir_with_parents (dir_name, 0777); |
155 | if (result < 0) |
156 | { |
157 | g_free (dir_name); |
158 | |
159 | if (errno(*__errno_location ()) != EEXIST17) |
160 | return NULL((void*)0); |
161 | else |
162 | continue; |
163 | } |
164 | else |
165 | return dir_name; |
166 | } |
167 | while (TRUE(!(0))); |
168 | } |
169 | |
170 | static void |
171 | signal_handler (int sig) |
172 | { |
173 | clean_up_temporary_dir (FALSE(0)); |
174 | |
175 | signal (sig, SIG_DFL((__sighandler_t) 0)); |
176 | kill (getpid (), sig); |
177 | } |
178 | |
179 | void |
180 | screenshot_save_start (GdkPixbuf *pixbuf, |
181 | SaveFunction callback, |
182 | gpointer user_data) |
183 | { |
184 | GPid pid; |
185 | int parent_exit_notification[2]; |
186 | int pipe_from_child[2]; |
187 | |
188 | if (pipe (parent_exit_notification) == -1) |
189 | perror("pipe error"); |
190 | |
191 | if (pipe (pipe_from_child) == -1) |
192 | perror("pipe error"); |
193 | |
194 | parent_dir = make_temp_directory (); |
195 | if (parent_dir == NULL((void*)0)) |
196 | return; |
197 | |
198 | tmp_filename = g_build_filename (parent_dir, |
199 | _("Screenshot.png")gettext ("Screenshot.png"), |
200 | NULL((void*)0)); |
201 | save_callback = callback; |
202 | save_user_data = user_data; |
203 | |
204 | pid = fork (); |
205 | if (pid == 0) |
206 | { |
207 | GError *error = NULL((void*)0); |
208 | char c; |
209 | |
210 | signal (SIGINT2, signal_handler); |
211 | signal (SIGTERM15, signal_handler); |
212 | |
213 | close (parent_exit_notification [1]); |
214 | close (pipe_from_child [0]); |
215 | |
216 | if (! gdk_pixbuf_save (pixbuf, tmp_filename, |
217 | "png", &error, |
218 | "tEXt::Software", "cafe-screenshot", |
219 | NULL((void*)0))) |
220 | { |
221 | if (error && error->message) |
222 | if (write (pipe_from_child[1], error->message, strlen (error->message)) == -1) |
223 | perror("write error"); |
This statement is never executed | |
224 | else |
225 | #define ERROR_MESSAGEgettext ("Unknown error saving screenshot to disk") _("Unknown error saving screenshot to disk")gettext ("Unknown error saving screenshot to disk") |
226 | if (write (pipe_from_child[1], ERROR_MESSAGEgettext ("Unknown error saving screenshot to disk"), strlen (ERROR_MESSAGEgettext ("Unknown error saving screenshot to disk"))) == -1) |
227 | perror("write error"); |
228 | } |
229 | /* By closing the pipe, we let the main process know that we're |
230 | * done saving it. */ |
231 | close (pipe_from_child[1]); |
232 | |
233 | if (read (parent_exit_notification[0], &c, 1) == -1) |
234 | perror("read error"); |
235 | |
236 | clean_up_temporary_dir (FALSE(0)); |
237 | _exit (0); |
238 | } |
239 | else if (pid > 0) |
240 | { |
241 | GIOChannel *channel; |
242 | |
243 | close (parent_exit_notification[0]); |
244 | close (pipe_from_child[1]); |
245 | |
246 | channel = g_io_channel_unix_new (pipe_from_child[0]); |
247 | g_io_add_watch (channel, |
248 | G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, |
249 | read_pipe_from_child, |
250 | NULL((void*)0)); |
251 | g_io_channel_unref (channel); |
252 | g_child_watch_add (pid, child_done_notification, NULL((void*)0)); |
253 | } |
254 | else |
255 | /* George awesomely wrote code originally to handle the |
256 | * could-not-fork case synchronously. I'm not copying it, as I'm |
257 | * guessing that the system is pretty hosed if that's the case. |
258 | * However, he gets major kudos for trying. (-: |
259 | */ |
260 | g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "screenshot-save.c" , 260, ((const char*) (__func__)), ((void*)0)); } while (0); |
261 | } |
262 | |
263 | const char * |
264 | screenshot_save_get_filename (void) |
265 | { |
266 | return tmp_filename; |
267 | } |