1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  *
16  * See the COPYING file for license information.
17  *
18  * Guillaume Chazarain <guichaz@gmail.com>
19  */
20 
21 /************************************************
22  * Files decompression (images and collections) *
23  ************************************************/
24 
25 #include <unistd.h>             /* read(), pipe(), fork(), execlp() ... */
26 #include <sys/types.h>          /* pid_t */
27 #include <signal.h>             /* kill(), SIGTERM */
28 #include <stdio.h>              /* perror() */
29 #include <sys/wait.h>           /* waitpid() */
30 #include <errno.h>              /* errno */
31 
32 #include "gliv.h"
33 #include "decompression.h"
34 #include "collection.h"
35 #include "loading.h"
36 
37 typedef struct {
38     gchar *ext;
39     gchar *cmd;
40 } decompressor;
41 
42 /* Read from the child process. */
read_image(gint fd,GError ** error)43 static GdkPixbuf *read_image(gint fd, GError ** error)
44 {
45     guchar buf[4096];
46     GdkPixbuf *pixbuf;
47     GdkPixbufLoader *loader;
48     gsize size;
49 
50     loader = gdk_pixbuf_loader_new();
51 
52     while ((size = read(fd, buf, sizeof(buf))) > 0 || (errno == EINTR)) {
53         if (size > 0 &&
54             gdk_pixbuf_loader_write(loader, buf, size, error) == FALSE)
55             return NULL;
56     }
57 
58     if (gdk_pixbuf_loader_close(loader, error) == FALSE)
59         return NULL;
60 
61     pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
62     if (pixbuf != NULL)
63         gdk_pixbuf_ref(pixbuf);
64 
65     g_object_unref(loader);
66     return pixbuf;
67 }
68 
69 /* Exec: "%cmd -c %filename" to send the output to stdout, the parent. */
decomp_file(const gchar * cmd,const gchar * filename,gint fd)70 static void decomp_file(const gchar * cmd, const gchar * filename, gint fd)
71 {
72     if (dup2(fd, STDOUT_FILENO) < 0) {
73         perror("dup2");
74         return;
75     }
76 
77     if (execlp(cmd, cmd, "-c", filename, NULL) < 0) {
78         perror("execlp");
79         return;
80     }
81 }
82 
init_decompression(const gchar * filename,gint * fd,pid_t * pid_ptr)83 static gboolean init_decompression(const gchar * filename, gint * fd,
84                                    pid_t * pid_ptr)
85 {
86     /* *INDENT-OFF* */
87     static decompressor all_decomp[] = {
88         { "bz2", "bunzip2"    },
89         { "gz",  "gunzip"     },
90         { "z",   "uncompress" },
91         { NULL,  NULL         }
92     };
93     /* *INDENT-ON* */
94 
95     gint filedes[2];
96     pid_t pid;
97     decompressor *decomp;
98     const gchar *ext;
99 
100     /* Find the appropriate decompressor. */
101     ext = get_extension(filename);
102     if (ext == NULL)
103         return FALSE;
104 
105     for (decomp = all_decomp; decomp->ext != NULL; decomp++)
106         if (!g_ascii_strcasecmp(decomp->ext, ext))
107             break;
108 
109     if (decomp->ext == NULL)
110         /* Decompressor not found. */
111         return FALSE;
112 
113     if (pipe(filedes) < 0) {
114         perror("pipe");
115         return FALSE;
116     }
117 
118     pid = fork();
119     if (pid < 0) {
120         perror("fork");
121         return FALSE;
122     }
123 
124     if (pid == 0) {
125         /* Child */
126         close(filedes[0]);
127         decomp_file(decomp->cmd, filename, filedes[1]);
128         return FALSE;
129     }
130 
131     /* Parent */
132     close(filedes[1]);
133     *fd = filedes[0];
134     *pid_ptr = pid;
135     return TRUE;
136 }
137 
load_compressed_pixbuf(const gchar * filename,GError ** error)138 GdkPixbuf *load_compressed_pixbuf(const gchar * filename, GError ** error)
139 {
140     gint fd;
141     pid_t pid;
142     GdkPixbuf *pixbuf;
143 
144     if (init_decompression(filename, &fd, &pid) == FALSE)
145         return NULL;
146 
147     pixbuf = read_image(fd, error);
148     close(fd);
149     waitpid(pid, NULL, 0);
150 
151     return pixbuf;
152 }
153 
load_compressed_collection(const gchar * filename)154 gint load_compressed_collection(const gchar * filename)
155 {
156     gint fd;
157     pid_t pid;
158     gint nb_inserted;
159     FILE *file;
160 
161     if (init_decompression(filename, &fd, &pid) == FALSE)
162         return 0;
163 
164     file = fdopen(fd, "r");
165     if (file == NULL) {
166         perror(filename);
167         kill(pid, SIGTERM);
168         nb_inserted = 0;
169     } else
170         nb_inserted = load_dot_gliv_from_file(filename, file);
171 
172     fclose(file);
173     waitpid(pid, NULL, 0);
174 
175     return nb_inserted;
176 }
177