First usable command-line UI version.
[captive.git] / src / install / acquire / cabinet.c
1 /* $Id$
2  * cabextract interface for acquiration installation utility
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
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, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "cabinet.h"    /* self */
23 #include <glib/gmessages.h>
24 #include <libgnomevfs/gnome-vfs-file-size.h>
25 #include <libgnomevfs/gnome-vfs-ops.h>
26 #include "cabextract/cabextract.h"
27 #include "captivemodid.h"
28 #include "moduriload.h"
29 #include <sys/mman.h>
30 #include <unistd.h>
31 #include "main.h"
32
33 #include <captive/macros.h>
34
35
36 void acquire_cabinet_seek(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
37 {
38         g_return_if_fail(acquire_cabinet!=NULL);
39
40         (*ui_search_is_aborted)();
41
42         acquire_cabinet->offset=offset;
43 }
44
45 void acquire_cabinet_seek_skip(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
46 {
47         g_return_if_fail(acquire_cabinet!=NULL);
48
49         (*ui_search_is_aborted)();
50
51         acquire_cabinet->offset+=offset;
52 }
53
54 GnomeVFSFileOffset acquire_cabinet_tell(struct acquire_cabinet *acquire_cabinet)
55 {
56         g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
57
58         (*ui_search_is_aborted)();
59
60         return acquire_cabinet->offset;
61 }
62
63 #define ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,pos)     (!(acquire_cabinet)->base_cached \
64                                                            || (acquire_cabinet)->base_cached[(pos)/8] &  1<<((pos)&7))
65 #define ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,pos) ((acquire_cabinet)->base_cached[(pos)/8] |= 1<<((pos)&7))
66
67 GnomeVFSResult acquire_cabinet_read
68                 (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read)
69 {
70 GnomeVFSFileOffset offset_start,offset_end,read_behind;
71 GnomeVFSResult errvfsresult;
72 GnomeVFSFileSize bytes_read_now;
73
74         g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
75         g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
76
77         if ((*ui_search_is_aborted)())
78                 return GNOME_VFS_ERROR_INTERRUPTED;
79
80         bytes=MAX(0,MIN(bytes,acquire_cabinet->size-acquire_cabinet->offset));
81         if (!bytes)
82                 return GNOME_VFS_ERROR_EOF;
83
84         *bytes_read=0;
85         while (bytes) {
86                 read_behind =acquire_cabinet->offset+bytes;
87
88                 /* GnomeVFS block transfer: */
89                 offset_start=acquire_cabinet->offset;
90                 offset_end  =acquire_cabinet->offset;
91                 while (offset_end<read_behind && !ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
92                         offset_end++;
93                 if (offset_end>offset_start) {
94                         if (GNOME_VFS_OK!=(errvfsresult=gnome_vfs_seek(acquire_cabinet->handle,GNOME_VFS_SEEK_START,offset_start)))
95                                 return errvfsresult;
96                         errvfsresult=gnome_vfs_read(acquire_cabinet->handle,
97                                         acquire_cabinet->base+offset_start,offset_end-offset_start,&bytes_read_now);
98                         if (errvfsresult!=GNOME_VFS_OK)
99                                 return errvfsresult;
100                         g_assert(bytes_read_now>0);
101                         while (bytes_read_now) {
102                                 ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,offset_start);
103                                 offset_start++;
104                                 bytes_read_now--;
105                                 }
106                         }
107
108                 /* Memory block transfer: */
109                 offset_start=acquire_cabinet->offset;
110                 offset_end  =acquire_cabinet->offset;
111                 while (offset_end<read_behind && ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
112                         offset_end++;
113                 memcpy(buffer,acquire_cabinet->base+offset_start,offset_end-offset_start);
114                 if (bytes_read)
115                         *bytes_read+=offset_end-offset_start;
116                 buffer+=offset_end-offset_start;
117                 bytes-=offset_end-offset_start;
118                 acquire_cabinet->offset=offset_end;
119                 }
120
121         return GNOME_VFS_OK;
122 }
123
124 static void acquire_cabinet_set_uri(struct acquire_cabinet *acquire_cabinet,GnomeVFSURI *uri)
125 {
126 GnomeVFSURI *uri_cabextract;
127
128         g_return_if_fail(acquire_cabinet!=NULL);
129         g_return_if_fail(uri!=NULL);
130
131         /* FIXME: HACK: Use proper 'cabextract' scheme after it gets implemented.
132          * GnomeVFS will return NULL on gnome_vfs_uri_new() with scheme not available.
133          */
134         uri_cabextract=gnome_vfs_uri_new("file://");
135         g_assert(uri_cabextract->parent==NULL);
136         /* Do not: g_assert(!strcmp(uri_cabextract->method_string,"file"));
137          *         uri_cabextract->method_string=g_strdup("cabextract");
138          * as it will just strip such anchor. FIXME: Why?
139          */
140
141         uri_cabextract->parent=gnome_vfs_uri_dup(uri);
142
143         acquire_cabinet->uri=uri_cabextract;
144         acquire_cabinet->filename=gnome_vfs_uri_to_string(acquire_cabinet->uri,GNOME_VFS_URI_HIDE_PASSWORD);
145 }
146
147 struct acquire_cabinet *acquire_cabinet_new_from_memory(gconstpointer file_base,size_t file_length,GnomeVFSURI *uri)
148 {
149 struct acquire_cabinet *r;
150
151         g_return_val_if_fail(file_base!=NULL,NULL);
152         g_return_val_if_fail(uri!=NULL,NULL);
153         
154         captive_new(r);
155         r->base=(/* de-const */ gpointer)file_base;
156         r->base_cached=NULL;
157         r->offset=0;
158         r->handle=NULL;
159         r->size=file_length;
160         acquire_cabinet_set_uri(r,uri);
161
162         return r;
163 }
164
165 struct acquire_cabinet *acquire_cabinet_new_from_handle(GnomeVFSHandle *handle,GnomeVFSFileInfo *file_info,GnomeVFSURI *uri)
166 {
167 struct acquire_cabinet *r;
168
169         g_return_val_if_fail(handle!=NULL,NULL);
170         g_return_val_if_fail(file_info!=NULL,NULL);
171         g_return_val_if_fail(uri!=NULL,NULL);
172         
173         captive_new(r);
174         if (MAP_FAILED==(r->base=mmap(
175                         NULL,CAPTIVE_ROUND_UP64(file_info->size,getpagesize()),PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0))) {
176                 g_free(r);
177                 g_return_val_if_reached(NULL);
178                 }
179         captive_new0n(r->base_cached,CAPTIVE_ROUND_UP64(file_info->size,8)/8);
180         r->offset=0;
181         r->handle=handle;
182         r->size=file_info->size;
183         acquire_cabinet_set_uri(r,uri);
184
185         return r;
186 }
187
188 void acquire_cabinet_free(struct acquire_cabinet *acquire_cabinet)
189 {
190         g_return_if_fail(acquire_cabinet!=NULL);
191
192         if (acquire_cabinet->base_cached) {
193                 munmap(acquire_cabinet->base,CAPTIVE_ROUND_UP64(acquire_cabinet->size,getpagesize()));  /* errors ignored */
194                 g_free(acquire_cabinet->base_cached);
195                 }
196         g_free((/* de-const */ gchar *)acquire_cabinet->filename);
197         gnome_vfs_uri_unref(acquire_cabinet->uri);
198         g_free(acquire_cabinet);
199 }
200
201 static struct file *file_write_fi_assertion;
202 static GByteArray *file_write_bytearray;
203
204 int file_write(struct file *fi, UBYTE *buf, size_t length)
205 {
206         g_return_val_if_fail(fi!=NULL,0);
207         g_return_val_if_fail(buf!=NULL || length==0,0);
208
209         g_return_val_if_fail(fi==file_write_fi_assertion,0);
210         g_return_val_if_fail(file_write_bytearray!=NULL,0);
211
212         if ((*ui_search_is_aborted)())
213                 return 0;
214
215         g_byte_array_append(file_write_bytearray,buf,length);
216
217         return 1;       /* success */
218 }
219
220 void acquire_cabinet_load(struct acquire_cabinet *acquire_cabinet)
221 {
222 struct cabinet *basecab;
223 struct file *filelist,*fi;
224
225         g_return_if_fail(acquire_cabinet!=NULL);
226
227         if ((*ui_search_is_aborted)())
228                 return;
229
230         basecab=find_cabs_in_file(acquire_cabinet);
231         if (!basecab)
232                 return;
233         if (basecab->next)
234                 return;
235         if (basecab->prevcab || basecab->nextcab)
236                 return;
237
238         filelist=process_files(basecab);
239
240         for (fi=filelist;fi;fi=fi->next) {
241 gpointer file_buffer;
242 GnomeVFSURI *uri_fi;
243
244                 if (!captivemodid_module_length_is_valid(fi->length))
245                         continue;
246
247                 if ((*ui_search_is_aborted)())
248                         return;
249
250                 file_write_fi_assertion=fi;
251                 file_write_bytearray=g_byte_array_new();
252                 extract_file(fi,
253                                 0,      /* lower; ignored now */
254                                 FALSE,  /* fix */
255                                 NULL);  /* dir; ignored now */
256                 if (fi->length!=file_write_bytearray->len) {
257                         g_byte_array_free(file_write_bytearray,
258                                         TRUE);  /* free_segment */
259                         continue;
260                         }
261                 file_buffer=g_byte_array_free(file_write_bytearray,
262                                 FALSE); /* free_segment */
263                 uri_fi=gnome_vfs_uri_append_file_name(acquire_cabinet->uri,fi->filename);
264                 mod_uri_load_file_from_memory(file_buffer,fi->length,uri_fi);
265                 gnome_vfs_uri_unref(uri_fi);
266                 g_free(file_buffer);
267     }
268 }