1 /*
2 Gpredict: Real-time satellite tracking and orbit prediction program
3
4 Copyright (C) 2001-2017 Alexandru Csete, OZ9AEC.
5
6 Authors: Alexandru Csete <oz9aec@gmail.com>
7
8 Comments, questions and bugreports should be submitted via
9 http://sourceforge.net/projects/gpredict/
10 More details can be found at the project home page:
11
12 http://gpredict.oz9aec.net/
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, visit http://www.fsf.org/
26 */
27 #ifdef HAVE_CONFIG_H
28 #include <build-config.h>
29 #endif
30 #include <gtk/gtk.h>
31
32 #include "gtk-sat-data.h"
33 #include "pass-to-txt.h"
34 #include "predict-tools.h"
35 #include "sat-cfg.h"
36 #include "sat-log.h"
37 #include "sat-pass-dialogs.h"
38 #include "gpredict-utils.h"
39 #include "save-pass.h"
40 #include "sgpsdp/sgp4sdp4.h"
41
42 static void file_changed(GtkWidget * widget, gpointer data);
43 static void save_pass_exec(GtkWidget * parent,
44 pass_t * pass, qth_t * qth,
45 const gchar * savedir, const gchar * savefile,
46 gint format, gint contents);
47 static void save_passes_exec(GtkWidget * parent,
48 GSList * passes, qth_t * qth,
49 const gchar * savedir, const gchar * savefile,
50 gint format, gint contents);
51 static void save_to_file(GtkWidget * parent, const gchar * fname,
52 const gchar * data);
53
54 enum pass_content_e {
55 PASS_CONTENT_ALL = 0,
56 PASS_CONTENT_TABLE,
57 PASS_CONTENT_DATA,
58 };
59
60 enum passes_content_e {
61 PASSES_CONTENT_FULL = 0,
62 PASSES_CONTENT_SUM,
63 };
64
65 #define SAVE_FORMAT_TXT 0
66
67 /**
68 * Save a satellite pass.
69 *
70 * @param toplevel Pointer to the parent dialogue window
71 *
72 * This function is called from the button click handler of the satellite pass
73 * dialogue when the user presses the Save button.
74 *
75 * The function opens the Save Pass dialogue asking the user for where to save
76 * the pass and in which format. When the user has made the required choices,
77 * the function uses the lower level functions to save the pass information
78 * to a file.
79 *
80 * \note All the relevant data are attached to the parent dialogue window.
81 */
save_pass(GtkWidget * parent)82 void save_pass(GtkWidget * parent)
83 {
84 GtkWidget *dialog;
85 GtkWidget *grid;
86 GtkWidget *dirchooser;
87 GtkWidget *filchooser;
88 GtkWidget *contents;
89 GtkWidget *label;
90 gint response;
91 pass_t *pass;
92 qth_t *qth;
93 gchar *savedir = NULL;
94 gchar *savefile;
95 gint cont;
96
97
98 /* get data attached to parent */
99 qth = (qth_t *) g_object_get_data(G_OBJECT(parent), "qth");
100 pass = (pass_t *) g_object_get_data(G_OBJECT(parent), "pass");
101
102 /* create the dialog */
103 dialog =
104 gtk_dialog_new_with_buttons(_("Save Pass Details"), GTK_WINDOW(parent),
105 GTK_DIALOG_MODAL |
106 GTK_DIALOG_DESTROY_WITH_PARENT,
107 "_Cancel", GTK_RESPONSE_REJECT,
108 "_Save", GTK_RESPONSE_ACCEPT, NULL);
109 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
110
111 /* create the table */
112 grid = gtk_grid_new();
113 gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
114 gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
115 gtk_container_set_border_width(GTK_CONTAINER(grid), 10);
116
117 /* directory chooser */
118 label = gtk_label_new(_("Save in folder:"));
119 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START,
120 "valign", GTK_ALIGN_CENTER, NULL);
121 gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
122
123 dirchooser = gtk_file_chooser_button_new(_("Select a folder"),
124 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
125 savedir = sat_cfg_get_str(SAT_CFG_STR_PRED_SAVE_DIR);
126 if (savedir)
127 {
128 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirchooser),
129 savedir);
130 g_free(savedir);
131 }
132 else
133 {
134 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirchooser),
135 g_get_home_dir());
136 }
137 gtk_grid_attach(GTK_GRID(grid), dirchooser, 1, 0, 1, 1);
138
139 /* file name */
140 label = gtk_label_new(_("Save using file name:"));
141 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START,
142 "valign", GTK_ALIGN_CENTER, NULL);
143 gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
144
145 filchooser = gtk_entry_new();
146 gtk_entry_set_max_length(GTK_ENTRY(filchooser), 100);
147 g_signal_connect(filchooser, "changed", G_CALLBACK(file_changed), dialog);
148 gtk_grid_attach(GTK_GRID(grid), filchooser, 1, 1, 1, 1);
149
150 /* use satellite name + orbit num as default; replace invalid characters
151 with dash */
152 savefile = g_strdup_printf("%s-%d", pass->satname, pass->orbit);
153 savefile = g_strdelimit(savefile, " ", '-');
154 savefile = g_strdelimit(savefile, "!?/\\()*&%$#@[]{}=+<>,.|:;", '_');
155 gtk_entry_set_text(GTK_ENTRY(filchooser), savefile);
156 g_free(savefile);
157
158 /* file contents */
159 label = gtk_label_new(_("File contents:"));
160 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START,
161 "valign", GTK_ALIGN_CENTER, NULL);
162 gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1);
163
164 contents = gtk_combo_box_text_new();
165 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(contents),
166 _("Info+header+data"));
167 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(contents),
168 _("Header + data"));
169 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(contents),
170 _("Data only"));
171 gtk_combo_box_set_active(GTK_COMBO_BOX(contents),
172 sat_cfg_get_int(SAT_CFG_INT_PRED_SAVE_CONTENTS));
173 gtk_grid_attach(GTK_GRID(grid), contents, 1, 2, 1, 1);
174
175 gtk_widget_show_all(grid);
176 gtk_container_add(GTK_CONTAINER
177 (gtk_dialog_get_content_area(GTK_DIALOG(dialog))), grid);
178
179 /* run the dialog */
180 response = gtk_dialog_run(GTK_DIALOG(dialog));
181
182 switch (response)
183 {
184 /* user clicked the save button */
185 case GTK_RESPONSE_ACCEPT:
186
187 /* get file and directory */
188 savedir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dirchooser));
189 savefile = g_strdup(gtk_entry_get_text(GTK_ENTRY(filchooser)));
190 cont = gtk_combo_box_get_active(GTK_COMBO_BOX(contents));
191
192 /* call saver */
193 save_pass_exec(dialog, pass, qth, savedir, savefile, SAVE_FORMAT_TXT,
194 cont);
195
196 /* store new settings */
197 sat_cfg_set_str(SAT_CFG_STR_PRED_SAVE_DIR, savedir);
198 sat_cfg_set_int(SAT_CFG_INT_PRED_SAVE_CONTENTS, cont);
199
200 /* clean up */
201 g_free(savedir);
202 g_free(savefile);
203 break;
204
205 /* cancel */
206 default:
207 break;
208
209 }
210
211 gtk_widget_destroy(dialog);
212 }
213
214 /**
215 * Save a satellite passes.
216 *
217 * @param toplevel Pointer to the parent dialogue window
218 *
219 * This function is called from the button click handler of the satellite passes
220 * dialogue when the user presses the Save button.
221 *
222 * The function opens the Save Pass dialogue asking the user for where to save
223 * the data and in which format. When the user has made the required choices,
224 * the function uses the lower level functions to save the pass information
225 * to a file.
226 *
227 * @note All the relevant data are attached to the parent dialogue window.
228 */
save_passes(GtkWidget * parent)229 void save_passes(GtkWidget * parent)
230 {
231 GtkWidget *dialog;
232 GtkWidget *grid;
233 GtkWidget *dirchooser;
234 GtkWidget *filchooser;
235 GtkWidget *contents;
236 GtkWidget *label;
237 gint response;
238 GSList *passes;
239 gchar *sat;
240 qth_t *qth;
241 gchar *savedir = NULL;
242 gchar *savefile;
243 gint cont;
244
245 /* get data attached to parent */
246 sat = (gchar *) g_object_get_data(G_OBJECT(parent), "sat");
247 qth = (qth_t *) g_object_get_data(G_OBJECT(parent), "qth");
248 passes = (GSList *) g_object_get_data(G_OBJECT(parent), "passes");
249
250 /* create the dialog */
251 dialog = gtk_dialog_new_with_buttons(_("Save Passes"), GTK_WINDOW(parent),
252 GTK_DIALOG_MODAL |
253 GTK_DIALOG_DESTROY_WITH_PARENT,
254 "_Cancel", GTK_RESPONSE_REJECT,
255 "_Save", GTK_RESPONSE_ACCEPT, NULL);
256 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
257
258 /* create the table */
259 grid = gtk_grid_new();
260 gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
261 gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
262 gtk_container_set_border_width(GTK_CONTAINER(grid), 10);
263
264 /* directory chooser */
265 label = gtk_label_new(_("Save in folder:"));
266 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START,
267 "valign", GTK_ALIGN_CENTER, NULL);
268 gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
269
270 dirchooser = gtk_file_chooser_button_new(_("Select a folder"),
271 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
272 savedir = sat_cfg_get_str(SAT_CFG_STR_PRED_SAVE_DIR);
273 if (savedir)
274 {
275 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirchooser),
276 savedir);
277 g_free(savedir);
278 }
279 else
280 {
281 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirchooser),
282 g_get_home_dir());
283 }
284 gtk_grid_attach(GTK_GRID(grid), dirchooser, 1, 0, 1, 1);
285
286 /* file name */
287 label = gtk_label_new(_("Save using file name:"));
288 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START,
289 "valign", GTK_ALIGN_CENTER, NULL);
290 gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
291
292 filchooser = gtk_entry_new();
293 gtk_entry_set_max_length(GTK_ENTRY(filchooser), 100);
294 g_signal_connect(filchooser, "changed", G_CALLBACK(file_changed), dialog);
295 gtk_grid_attach(GTK_GRID(grid), filchooser, 1, 1, 1, 1);
296
297 /* use satellite name + orbit num as default; replace invalid characters
298 with dash */
299 savefile = g_strdup_printf("%s-passes", sat);
300 savefile = g_strdelimit(savefile, " ", '-');
301 savefile = g_strdelimit(savefile, "!?/\\()*&%$#@[]{}=+<>,.|:;", '_');
302 gtk_entry_set_text(GTK_ENTRY(filchooser), savefile);
303 g_free(savefile);
304
305 /* file contents */
306 label = gtk_label_new(_("File contents:"));
307 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START,
308 "valign", GTK_ALIGN_CENTER, NULL);
309 gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1);
310
311 contents = gtk_combo_box_text_new();
312 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(contents),
313 _("Complete report"));
314 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(contents), _("Summary"));
315 gtk_combo_box_set_active(GTK_COMBO_BOX(contents), 0);
316 gtk_grid_attach(GTK_GRID(grid), contents, 1, 2, 1, 1);
317
318 gtk_widget_show_all(grid);
319 gtk_container_add(GTK_CONTAINER
320 (gtk_dialog_get_content_area(GTK_DIALOG(dialog))), grid);
321
322 /* run the dialog */
323 response = gtk_dialog_run(GTK_DIALOG(dialog));
324
325 switch (response)
326 {
327 /* user clicked the save button */
328 case GTK_RESPONSE_ACCEPT:
329
330 /* get file and directory */
331 savedir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dirchooser));
332 savefile = g_strdup(gtk_entry_get_text(GTK_ENTRY(filchooser)));
333 cont = gtk_combo_box_get_active(GTK_COMBO_BOX(contents));
334
335 /* call saver */
336 save_passes_exec(dialog, passes, qth, savedir, savefile,
337 SAVE_FORMAT_TXT, cont);
338
339 /* store new settings */
340 sat_cfg_set_str(SAT_CFG_STR_PRED_SAVE_DIR, savedir);
341
342 /* clean up */
343 g_free(savedir);
344 g_free(savefile);
345 break;
346
347 /* cancel */
348 default:
349 break;
350 }
351 gtk_widget_destroy(dialog);
352 }
353
354 /**
355 * Manage file name changes.
356 *
357 * @param widget The GtkEntry that received the signal
358 * @param data User data (not used).
359 *
360 * This function is called when the contents of the file name entry changes.
361 * It validates all characters and invalid chars are deleted.
362 * The function sets the state of the Save button according to the validity
363 * of the current file name.
364 */
file_changed(GtkWidget * widget,gpointer data)365 static void file_changed(GtkWidget * widget, gpointer data)
366 {
367 gchar *entry, *end, *j;
368 gint len, pos;
369 const gchar *text;
370 GtkWidget *dialog = GTK_WIDGET(data);
371
372 /* ensure that only valid characters are entered
373 (stolen from xlog, tnx pg4i)
374 */
375 entry = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
376 if ((len = g_utf8_strlen(entry, -1)) > 0)
377 {
378 end = entry + g_utf8_strlen(entry, -1);
379 for (j = entry; j < end; ++j)
380 {
381 if (!gpredict_legal_char(*j))
382 {
383 gdk_beep();
384 pos = gtk_editable_get_position(GTK_EDITABLE(widget));
385 gtk_editable_delete_text(GTK_EDITABLE(widget), pos, pos + 1);
386 }
387 }
388 }
389
390 /* step 2: if name seems all right, enable OK button */
391 text = gtk_entry_get_text(GTK_ENTRY(widget));
392
393 if (g_utf8_strlen(text, -1) > 0)
394 {
395 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
396 GTK_RESPONSE_ACCEPT, TRUE);
397 }
398 else
399 {
400 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
401 GTK_RESPONSE_ACCEPT, FALSE);
402 }
403 }
404
405 /**
406 * Save data to file.
407 *
408 * @param parent Parent window (needed for error dialogs).
409 * @param pass The pass data to save.
410 * @param qth The observer data
411 * @param savedir The directory where data should be saved.
412 * @param savefile The file where data should be saved.
413 * @param format The file format
414 * @param contents The contents defining whether to save headers and such.
415 *
416 * This is the function that does the actual saving to a data file once all
417 * required information has been gathered (i.e. file name, format, contents).
418 * The function does some last minute checking while saving and provides
419 * error messages if anything fails during the process.
420 *
421 * @note The formatting is done by external functions according to the selected
422 * file format.
423 */
save_passes_exec(GtkWidget * parent,GSList * passes,qth_t * qth,const gchar * savedir,const gchar * savefile,gint format,gint contents)424 static void save_passes_exec(GtkWidget * parent,
425 GSList * passes, qth_t * qth,
426 const gchar * savedir, const gchar * savefile,
427 gint format, gint contents)
428 {
429 gchar *fname;
430 gchar *pgheader;
431 gchar *tblheader;
432 gchar *tblcontents;
433 gchar *buff = NULL;
434 gchar *data = NULL;
435 pass_t *pass;
436 gint fields;
437 guint i, n;
438
439 switch (format)
440 {
441 case SAVE_FORMAT_TXT:
442
443 /* prepare full file name */
444 fname =
445 g_strconcat(savedir, G_DIR_SEPARATOR_S, savefile, ".txt", NULL);
446
447 /* get visible columns for summary */
448 fields = sat_cfg_get_int(SAT_CFG_INT_PRED_MULTI_COL);
449
450 /* create file contents */
451 pgheader = passes_to_txt_pgheader(passes, qth, fields);
452 tblheader = passes_to_txt_tblheader(passes, qth, fields);
453 tblcontents = passes_to_txt_tblcontents(passes, qth, fields);
454
455 data = g_strconcat(pgheader, tblheader, tblcontents, NULL);
456
457 g_free(pgheader);
458 g_free(tblheader);
459 g_free(tblcontents);
460
461 if (contents == PASSES_CONTENT_FULL)
462 {
463 fields = sat_cfg_get_int(SAT_CFG_INT_PRED_SINGLE_COL);
464 n = g_slist_length(passes);
465
466 for (i = 0; i < n; i++)
467 {
468
469 pass = PASS(g_slist_nth_data(passes, i));
470
471 tblheader = pass_to_txt_tblheader(pass, qth, fields);
472 tblcontents = pass_to_txt_tblcontents(pass, qth, fields);
473 buff = g_strdup_printf("%s\n Orbit %d\n%s%s",
474 data, pass->orbit,
475 tblheader, tblcontents);
476 g_free(data);
477 data = g_strdup(buff);
478 g_free(buff);
479
480 }
481 }
482
483 /* save data */
484 save_to_file(parent, fname, data);
485 g_free(data);
486 g_free(fname);
487 break;
488
489 default:
490 sat_log_log(SAT_LOG_LEVEL_ERROR,
491 _("%s: Invalid file format: %d"), __func__, format);
492 break;
493 }
494 }
495
496 /**
497 * Save data to file.
498 *
499 * @param parent Parent window (needed for error dialogs).
500 * @param passes The pass data to save.
501 * @param qth The observer data
502 * @param savedir The directory where data should be saved.
503 * @param savefile The file where data should be saved.
504 * @param format The file format
505 * @param contents The contents defining whether to save headers and such.
506 *
507 * This is the function that does the actual saving to a data file once all
508 * required information has been gathered (i.e. file name, format, contents).
509 * The function does some last minute checking while saving and provides
510 * error messages if anything fails during the process.
511 *
512 * @note The formatting is done by external functions according to the selected
513 * file format.
514 */
save_pass_exec(GtkWidget * parent,pass_t * pass,qth_t * qth,const gchar * savedir,const gchar * savefile,gint format,gint contents)515 static void save_pass_exec(GtkWidget * parent,
516 pass_t * pass, qth_t * qth,
517 const gchar * savedir, const gchar * savefile,
518 gint format, gint contents)
519 {
520 gchar *fname;
521 gchar *pgheader;
522 gchar *tblheader;
523 gchar *tblcontents;
524 gchar *buff = NULL;
525 gchar *data = NULL;
526 gint fields;
527
528 switch (format)
529 {
530 case SAVE_FORMAT_TXT:
531
532 /* prepare full file name */
533 fname =
534 g_strconcat(savedir, G_DIR_SEPARATOR_S, savefile, ".txt", NULL);
535
536 /* get visible columns */
537 fields = sat_cfg_get_int(SAT_CFG_INT_PRED_SINGLE_COL);
538
539 /* create file contents */
540 pgheader = pass_to_txt_pgheader(pass, qth, fields);
541 tblheader = pass_to_txt_tblheader(pass, qth, fields);
542 tblcontents = pass_to_txt_tblcontents(pass, qth, fields);
543
544 /* Add page header if selected */
545 if (contents == PASS_CONTENT_ALL)
546 {
547 data = g_strdup(pgheader);
548 }
549
550 /* Add table header if selected */
551 if ((contents == PASS_CONTENT_ALL) || (contents == PASS_CONTENT_TABLE))
552 {
553 if (data != NULL)
554 {
555 buff = g_strdup(data);
556 g_free(data);
557 data = g_strconcat(buff, tblheader, NULL);
558 g_free(buff);
559 }
560 else
561 {
562 data = g_strdup(tblheader);
563 }
564
565 }
566
567 /* Add data */
568 if (data != NULL)
569 {
570 buff = g_strdup(data);
571 g_free(data);
572 data = g_strconcat(buff, tblcontents, NULL);
573 g_free(buff);
574 }
575 else
576 {
577 data = g_strdup(tblcontents);
578 }
579
580 /* save data */
581 save_to_file(parent, fname, data);
582
583 /* clean up memory */
584 g_free(fname);
585 g_free(data);
586 g_free(pgheader);
587 g_free(tblheader);
588 g_free(tblcontents);
589
590 break;
591
592 default:
593 sat_log_log(SAT_LOG_LEVEL_ERROR,
594 _("%s: Invalid file format: %d"), __func__, format);
595 break;
596 }
597 }
598
save_to_file(GtkWidget * parent,const gchar * fname,const gchar * data)599 static void save_to_file(GtkWidget * parent, const gchar * fname,
600 const gchar * data)
601 {
602 GIOChannel *chan;
603 GError *err = NULL;
604 GtkWidget *dialog;
605 gsize count;
606
607 /* create file */
608 chan = g_io_channel_new_file(fname, "w", &err);
609 if (err != NULL)
610 {
611 sat_log_log(SAT_LOG_LEVEL_ERROR,
612 _("%s: Could not create file %s (%s)"),
613 __func__, fname, err->message);
614
615 /* error dialog */
616 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
617 GTK_DIALOG_MODAL |
618 GTK_DIALOG_DESTROY_WITH_PARENT,
619 GTK_MESSAGE_ERROR,
620 GTK_BUTTONS_CLOSE,
621 _("Could not create file %s\n\n%s"),
622 fname, err->message);
623 gtk_dialog_run(GTK_DIALOG(dialog));
624 gtk_widget_destroy(dialog);
625
626 /* clean up and return */
627 g_clear_error(&err);
628
629 return;
630 }
631
632 /* save contents to file */
633 g_io_channel_write_chars(chan, data, -1, &count, &err);
634 if (err != NULL)
635 {
636 sat_log_log(SAT_LOG_LEVEL_ERROR,
637 _("%s: An error occurred while saving data to %s (%s)"),
638 __func__, fname, err->message);
639
640 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
641 GTK_DIALOG_MODAL |
642 GTK_DIALOG_DESTROY_WITH_PARENT,
643 GTK_MESSAGE_ERROR,
644 GTK_BUTTONS_CLOSE,
645 _
646 ("An error occurred while saving data to %s\n\n%s"),
647 fname, err->message);
648 gtk_dialog_run(GTK_DIALOG(dialog));
649 gtk_widget_destroy(dialog);
650 g_clear_error(&err);
651 }
652 else
653 {
654 sat_log_log(SAT_LOG_LEVEL_DEBUG,
655 _("%s: Written %d characters to %s"),
656 __func__, count, fname);
657 }
658
659 /* close file, we don't care about errors here */
660 g_io_channel_shutdown(chan, TRUE, NULL);
661 g_io_channel_unref(chan);
662
663 }
664