2 * Copyright (c) 2008,2009 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
21 %module "Amanda::Archive"
22 %include "amglue/amglue.swg"
23 %include "exception.i"
26 %include "Amanda/Archive.pod"
33 /* Support code (not directly available from perl) */
35 #define AMANDA_ARCHIVE_ERROR_DOMAIN "Amanda archive"
37 /* A C object to contain all of the relevant callbacks and other state during a
38 * read operation; this becomes the user_data during the read */
39 typedef struct perl_read_data_s {
44 amar_attr_handling_t *handling_array;
57 perl_read_data_t *dat = user_data;
64 g_assert(dat->file_start_sub != NULL);
70 XPUSHs(dat->user_data);
71 XPUSHs(sv_2mortal(newSViv(filenum)));
72 XPUSHs(sv_2mortal(newSVpvn(filename, filename_len)));
75 count = call_sv(dat->file_start_sub, G_EVAL|G_SCALAR);
80 croak("file_start_sub returned nothing");
84 /* if it's the string "IGNORE", then ignore it */
86 static const char *ign = "IGNORE";
87 char *rvstr = SvPV(rv, len);
88 if (strlen(ign) == len && 0 == strncmp(ign, rvstr, len))
92 /* otherwise, keep the value */
94 *(SV **)(file_data) = SvREFCNT_inc(rv);
113 perl_read_data_t *dat = user_data;
115 g_assert(dat->file_finish_sub != NULL);
120 PUSHMARK(SP); XPUSHs(dat->user_data); XPUSHs(*(SV **)file_data);
121 XPUSHs(sv_2mortal(newSViv(filenum)));
122 XPUSHs(sv_2mortal(newSViv(truncated))); PUTBACK;
124 call_sv(dat->file_finish_sub, G_EVAL|G_DISCARD);
126 /* we're done with this file's file_data */
127 SvREFCNT_dec(*(SV **)file_data);
143 gpointer attrid_data,
151 perl_read_data_t *dat = user_data;
162 XPUSHs(dat->user_data);
163 XPUSHs(sv_2mortal(newSViv(filenum)));
164 XPUSHs((SV *)file_data);
165 XPUSHs(sv_2mortal(newSViv(attrid)));
167 XPUSHs((SV *)(*attr_data));
169 XPUSHs(&PL_sv_undef);
170 XPUSHs(sv_2mortal(newSVpvn(data, size)));
171 XPUSHs(sv_2mortal(newSViv(eoa)));
172 XPUSHs(sv_2mortal(newSViv(truncated)));
175 count = call_sv(attrid_data, G_EVAL|G_SCALAR);
180 croak("fragment callback returned nothing");
185 SvREFCNT_dec(*attr_data);
187 /* increment before decrement here, in case they're the same object */
189 SvREFCNT_dec(*attr_data);
201 /* generic function to recognize when a string+len represents a number and
202 * incidentally return the resulting value. Note that this does not handle
203 * negative numbers. */
205 is_number(char *str, int len, int *result)
211 if (!g_ascii_isdigit(*str)) return FALSE;
212 r = r * 10 + (int)(*str - '0');
226 /* Rename all of the below wrapper functions (suffixed with '_') for
227 * consumption by perl */
228 %rename(amar_new) amar_new_;
229 %rename(amar_close) amar_close_;
230 %rename(amar_new_file) amar_new_file_;
231 %rename(amar_file_close) amar_file_close_;
232 %rename(amar_new_attr) amar_new_attr_;
233 %rename(amar_attr_close) amar_attr_close_;
234 %rename(amar_attr_add_data_buffer) amar_attr_add_data_buffer_;
235 %rename(amar_attr_add_data_fd) amar_attr_add_data_fd_;
236 %rename(amar_read) amar_read_;
238 /* typemaps for the below */
239 %apply (char *STRING, int LENGTH) { (char *filename, gsize filename_len) };
240 %apply (char *STRING, int LENGTH) { (char *buffer, gsize size) };
241 %typemap(in) SV * "$1 = $input;"
243 %typemap(in) off_t *want_position (off_t position) {
244 if (SvTRUE($input)) {
251 %typemap(argout) off_t *want_position {
253 SP += argvi; PUTBACK;
254 $result = sv_2mortal(amglue_newSVi64(*$1));
255 SPAGAIN; SP -= argvi; argvi++;
261 /* Wrapper functions, mostly dealing with error handling */
263 amar_t *amar_new_(int fd, char *modestr) {
264 GError *error = NULL;
268 if (strcmp(modestr, ">") == 0)
270 else if (strcmp(modestr, "<") == 0)
273 croak("mode must be '<' or '>'");
275 if ((rv = amar_new(fd, mode, &error))) {
279 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
283 void amar_close_(amar_t *arch) {
284 GError *error = NULL;
285 if (!amar_close(arch, &error))
286 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
290 amar_new_file_(amar_t *arch, char *filename, gsize filename_len, off_t *want_position) {
291 GError *error = NULL;
293 g_assert(arch != NULL);
295 file = amar_new_file(arch, filename, filename_len, want_position, &error);
299 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
303 void amar_file_close_(amar_file_t *file) {
304 GError *error = NULL;
305 if (!amar_file_close(file, &error))
306 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
310 amar_new_attr_(amar_file_t *file, guint16 attrid) {
311 GError *error = NULL;
314 g_assert(file != NULL);
316 attr = amar_new_attr(file, attrid, &error);
320 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
324 void amar_attr_close_(amar_attr_t *attr) {
325 GError *error = NULL;
326 if (!amar_attr_close(attr, &error))
327 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
330 void amar_attr_add_data_buffer_(amar_attr_t *attr, char *buffer, gsize size, gboolean eoa) {
331 GError *error = NULL;
332 if (!amar_attr_add_data_buffer(attr, buffer, size, eoa, &error))
333 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
337 amar_attr_add_data_fd_(amar_attr_t *attr, int fd, gboolean eoa) {
338 GError *error = NULL;
339 size_t rv = amar_attr_add_data_fd(attr, fd, eoa, &error);
341 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
347 void amar_read_(amar_t *archive, SV *params_hashref) {
348 perl_read_data_t *dat = g_new0(perl_read_data_t, 1);
349 GError *error = NULL;
357 /* make sure we got a hashref */
358 if (!SvROK(params_hashref) || SvTYPE(SvRV(params_hashref)) != SVt_PVHV)
359 croak("read() expects a single hashref");
360 params = (HV *)SvRV(params_hashref);
361 len = hv_iterinit(params);
363 maxhandlers = hdl_idx = len;
364 dat->handling_array = g_new0(amar_attr_handling_t, len+1);
366 /* loop through the parameters */
367 while ((param = hv_iternext(params))) {
369 char *key = hv_iterkey(param, &keylen);
372 /* if it's a number, it's handling information for an attrid */
373 if (is_number(key, keylen, &attrid)) {
374 SV *val = hv_iterval(params, param);
379 if (!SvROK(val)) goto croak_hdl;
381 switch (SvTYPE(SvRV(val))) {
387 AV *arr = (AV *)SvRV(val);
390 if (av_len(arr) != 1) /* av_len == largest index, not length */
393 /* get the bufsize */
394 svp = av_fetch(arr, 0, 0);
397 bufsize = SvUV(*svp);
399 /* and the coderef */
400 svp = av_fetch(arr, 1, 0);
401 if (!SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVCV)
411 /* fill in the handling array, putting attrid 0 at the end, and
412 * filling in entries backward from there */
413 i = (attrid == 0)? maxhandlers : --hdl_idx;
414 dat->handling_array[i].attrid = attrid;
415 dat->handling_array[i].min_size = bufsize;
416 dat->handling_array[i].callback = read_frag_cb;
417 dat->handling_array[i].attrid_data = coderef;
418 SvREFCNT_inc(coderef);
422 croak("Expected CODEREF or [ MIN_SIZE, CODEREF ] for attrid %d", attrid);
425 #define key_compare(key, val, keylen) \
426 (keylen == sizeof(val)-1) && (0 == strncmp(key, val, keylen))
428 if (key_compare(key, "file_start", keylen)) {
429 SV *val = hv_iterval(params, param);
430 if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVCV)
431 croak("Expected a CODEREF for file_start");
432 dat->file_start_sub = val;
437 if (key_compare(key, "file_finish", keylen)) {
438 SV *val = hv_iterval(params, param);
439 if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVCV)
440 croak("Expected a CODEREF for file_finish");
441 dat->file_finish_sub = val;
446 if (key_compare(key, "user_data", keylen)) {
447 SV *val = hv_iterval(params, param);
448 dat->user_data = val;
453 croak("Invalid parameter named '%*s'", (int)keylen, key);
457 dat->user_data = &PL_sv_undef;
459 success = amar_read(archive, dat, dat->handling_array + hdl_idx,
460 dat->file_start_sub? read_start_file_cb : NULL,
461 dat->file_finish_sub? read_finish_file_cb : NULL,
464 /* now unreference and free everything we referenced earlier */
465 if (dat->file_start_sub)
466 SvREFCNT_dec(dat->file_start_sub);
467 if (dat->file_finish_sub)
468 SvREFCNT_dec(dat->file_finish_sub);
469 if (dat->user_data && dat->user_data != &PL_sv_undef)
470 SvREFCNT_dec(dat->user_data);
472 for (hdl_idx = 0; hdl_idx <= maxhandlers; hdl_idx++) {
473 if (dat->handling_array[hdl_idx].attrid_data)
474 SvREFCNT_dec(dat->handling_array[hdl_idx].attrid_data);
477 g_free(dat->handling_array);
480 /* if amar_read returned FALSE, then either we hit an internal
481 * error, or one of the perl callbacks raised an exception, and $@
485 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
493 /* now wrap those flat functions in Perl classes, depending on the perl
494 * refcounting to close objects in the right order */
497 package Amanda::Archive;
499 # Expose the Archive constructor at Amanda::Archive->new
502 Amanda::Archive::Archive->new(@_);
505 package Amanda::Archive::Archive;
508 my ($class, $fd, $mode) = @_;
509 my $arch = Amanda::Archive::amar_new($fd, $mode);
510 return bless (\$arch, $class);
516 Amanda::Archive::amar_close($$self);
527 my ($self, $filename, $want_offset) = @_;
528 return Amanda::Archive::File->new($self, $filename, $want_offset);
531 sub Amanda::Archive::Archive::read {
533 die "Archive is not open" unless ($$self);
534 # pass a hashref to the C code
536 Amanda::Archive::amar_read($$self, \%h);
539 package Amanda::Archive::File;
542 my ($class, $arch, $filename, $want_offset) = @_;
543 die "Archive is not open" unless ($$arch);
545 # note that posn is returned first by the SWIG wrapper
546 my ($file, $posn) = Amanda::Archive::amar_new_file($$arch, $filename, $want_offset);
547 return (bless([ $file, $arch ], $class), $posn);
549 my $file = Amanda::Archive::amar_new_file($$arch, $filename, $want_offset);
550 return bless([ $file, $arch ], $class);
557 Amanda::Archive::amar_file_close($self->[0]);
568 my ($self, $attrid) = @_;
569 return Amanda::Archive::Attr->new($self, $attrid);
572 package Amanda::Archive::Attr;
575 my ($class, $file, $attrid) = @_;
576 die "File is not open" unless ($file->[0]);
577 my $attr = Amanda::Archive::amar_new_attr($file->[0], $attrid);
578 return bless ([$attr, $file], $class);
584 Amanda::Archive::amar_attr_close($self->[0]);
595 my ($self, $data, $eoa) = @_;
596 die "Attr is not open" unless ($self->[0]);
597 Amanda::Archive::amar_attr_add_data_buffer($self->[0], $data, $eoa);
601 my ($self, $fd, $eoa) = @_;
602 die "Attr is not open" unless ($self->[0]);
603 return Amanda::Archive::amar_attr_add_data_fd($self->[0], $fd, $eoa);