2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2013 Krzysztof Foltman
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, either version 3 of the License, or
8 (at your option) any later version.
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
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "config-api.h"
26 #include <sys/types.h>
52 static void remove_item_if(gpointer p
);
54 struct cbox_tarfile
*cbox_tarfile_open(const char *pathname
, GError
**error
)
56 gboolean debug
= cbox_config_get_int("debug", "tarfile", 0);
57 gchar
*canonical
= realpath(pathname
, NULL
);
61 g_set_error(error
, G_FILE_ERROR
, g_file_error_from_errno (errno
), "cannot determine canonical name of '%s'", pathname
);
64 int fd
= open(canonical
, O_RDONLY
| O_LARGEFILE
);
69 g_set_error(error
, G_FILE_ERROR
, g_file_error_from_errno (errno
), "cannot open '%s'", pathname
);
72 GHashTable
*byname
= NULL
, *byname_nc
= NULL
;
74 byname
= g_hash_table_new(g_str_hash
, g_str_equal
);
75 byname_nc
= g_hash_table_new(g_str_hash
, g_str_equal
);
76 if (!byname
|| !byname_nc
)
79 struct cbox_tarfile
*tf
= calloc(1, sizeof(struct cbox_tarfile
));
83 tf
->items_byname
= byname
;
84 tf
->items_byname_nc
= byname_nc
;
86 tf
->file_pathname
= canonical
;
89 struct tar_record rec
;
90 int nbytes
= read(fd
, &rec
, sizeof(rec
));
91 if (nbytes
!= sizeof(rec
))
94 int len
= sizeof(rec
.name
);
95 while(len
> 0 && (rec
.name
[len
- 1] == ' ' || rec
.name
[len
- 1] == '\0'))
99 memcpy(sizetext
, rec
.size
, 12);
101 unsigned long long size
= strtoll(sizetext
, NULL
, 8);
103 // skip block if name is empty
106 struct cbox_taritem
*ti
= calloc(1, sizeof(struct cbox_taritem
));
110 if (len
>= 2 && rec
.name
[0] == '.' && rec
.name
[1] == '/')
112 ti
->filename
= g_strndup(rec
.name
+ offset
, len
- offset
);
113 ti
->filename_nc
= g_utf8_casefold(rec
.name
+ offset
, len
- offset
);
114 if (!ti
->filename
|| !ti
->filename_nc
)
116 ti
->offset
= lseek64(fd
, 0, SEEK_CUR
);
120 // Overwrite old items by the same name and/or same case-folded name
121 remove_item_if(g_hash_table_lookup(tf
->items_byname
, ti
->filename
));
122 remove_item_if(g_hash_table_lookup(tf
->items_byname_nc
, ti
->filename_nc
));
124 g_hash_table_insert(tf
->items_byname
, ti
->filename
, ti
);
125 g_hash_table_insert(tf
->items_byname_nc
, ti
->filename_nc
, ti
);
127 printf("name = %s len = %d offset = %d readsize = %d\n", ti
->filename
, len
, (int)ti
->offset
, (int)size
);
133 g_warning("Could not allocate memory for tar item %s", rec
.name
);
137 g_free(ti
->filename_nc
);
139 g_free(ti
->filename
);
143 lseek64(fd
, (size
+ 511) &~ 511, SEEK_CUR
);
149 g_hash_table_destroy(byname
);
151 g_hash_table_destroy(byname_nc
);
153 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Cannot allocate memory for tarfile data");
157 void remove_item_if(gpointer p
)
162 struct cbox_taritem
*ti
= p
;
163 // If all references (by name and by case-folded name) gone, remove the item
166 g_free(ti
->filename
);
167 g_free(ti
->filename_nc
);
172 struct cbox_taritem
*cbox_tarfile_get_item_by_name(struct cbox_tarfile
*tarfile
, const char *item_filename
, gboolean ignore_case
)
174 if (item_filename
[0] == '.' && item_filename
[1] == '/')
178 gchar
*folded
= g_utf8_casefold(item_filename
, -1);
179 struct cbox_taritem
*item
= g_hash_table_lookup(tarfile
->items_byname_nc
, folded
);
184 return g_hash_table_lookup(tarfile
->items_byname
, item_filename
);
187 int cbox_tarfile_openitem(struct cbox_tarfile
*tarfile
, struct cbox_taritem
*item
)
189 int fd
= open(tarfile
->file_pathname
, O_RDONLY
| O_LARGEFILE
);
191 lseek64(fd
, item
->offset
, SEEK_SET
);
195 void cbox_tarfile_closeitem(struct cbox_tarfile
*tarfile
, struct cbox_taritem
*item
, int fd
)
201 static void delete_foreach_func(gpointer key
, gpointer value
, gpointer user_data
)
203 struct cbox_taritem
*ti
= value
;
206 g_free(ti
->filename
);
207 g_free(ti
->filename_nc
);
212 void cbox_tarfile_destroy(struct cbox_tarfile
*tf
)
214 g_hash_table_foreach(tf
->items_byname
, delete_foreach_func
, NULL
);
215 g_hash_table_foreach(tf
->items_byname_nc
, delete_foreach_func
, NULL
);
217 g_hash_table_destroy(tf
->items_byname
);
218 g_hash_table_destroy(tf
->items_byname_nc
);
219 free(tf
->file_pathname
);
223 ////////////////////////////////////////////////////////////////////////////////
225 sf_count_t
tarfile_get_filelen(void *user_data
)
227 struct cbox_tarfile_sndstream
*ss
= user_data
;
229 return ss
->item
->size
;
232 sf_count_t
tarfile_seek(sf_count_t offset
, int whence
, void *user_data
)
234 struct cbox_tarfile_sndstream
*ss
= user_data
;
238 ss
->filepos
= offset
;
241 ss
->filepos
+= offset
;
244 ss
->filepos
= ss
->item
->size
;
247 if (((int64_t)ss
->filepos
) < 0)
249 if (((int64_t)ss
->filepos
) >= ss
->item
->size
)
250 ss
->filepos
= ss
->item
->size
;
254 sf_count_t
tarfile_read(void *ptr
, sf_count_t count
, void *user_data
)
256 struct cbox_tarfile_sndstream
*ss
= user_data
;
257 ssize_t len
= pread64(ss
->file
->fd
, ptr
, count
, ss
->item
->offset
+ ss
->filepos
);
263 sf_count_t
tarfile_tell(void *user_data
)
265 struct cbox_tarfile_sndstream
*ss
= user_data
;
269 struct SF_VIRTUAL_IO cbox_taritem_virtual_io
= {
270 .get_filelen
= tarfile_get_filelen
,
271 .seek
= tarfile_seek
,
272 .read
= tarfile_read
,
274 .tell
= tarfile_tell
,
277 SNDFILE
*cbox_tarfile_opensndfile(struct cbox_tarfile
*tarfile
, struct cbox_taritem
*item
, struct cbox_tarfile_sndstream
*stream
, SF_INFO
*sfinfo
)
279 stream
->file
= tarfile
;
282 return sf_open_virtual(&cbox_taritem_virtual_io
, SFM_READ
, sfinfo
, stream
);
285 ////////////////////////////////////////////////////////////////////////////////
287 struct cbox_tarpool
*cbox_tarpool_new(void)
289 struct cbox_tarpool
*pool
= calloc(1, sizeof(struct cbox_tarpool
));
290 pool
->files
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
294 struct cbox_tarfile
*cbox_tarpool_get_tarfile(struct cbox_tarpool
*pool
, const char *name
, GError
**error
)
296 gchar
*c
= realpath(name
, NULL
);
299 g_set_error(error
, G_FILE_ERROR
, g_file_error_from_errno (errno
), "cannot find a real path for '%s': %s", name
, strerror(errno
));
302 struct cbox_tarfile
*tf
= g_hash_table_lookup(pool
->files
, c
);
307 tf
= cbox_tarfile_open(c
, error
);
313 g_hash_table_insert(pool
->files
, c
, tf
);
318 void cbox_tarpool_release_tarfile(struct cbox_tarpool
*pool
, struct cbox_tarfile
*file
)
322 // XXXKF the insertion key is realpath(name) but the removal key is realpath(realpath(name))
323 // usually it shouldn't cause problems, but it should be improved.
324 if (!g_hash_table_lookup(pool
->files
, file
->file_pathname
))
325 g_warning("Removing tarfile %s not in the pool hash", file
->file_pathname
);
326 g_hash_table_remove(pool
->files
, file
->file_pathname
);
327 cbox_tarfile_destroy(file
);
331 void cbox_tarpool_destroy(struct cbox_tarpool
*pool
)
333 int nelems
= g_hash_table_size(pool
->files
);
335 g_warning("%d unfreed elements in tar pool %p.", nelems
, pool
);
336 g_hash_table_destroy(pool
->files
);