Kevin Jones - Archive.Tar-0.03

Documentation | Source

NAME

Archive.Tar - Read-only tar extraction in javascript

SYNOPSIS

  var tar = new Archive.Tar;
  tar.read("/stuff.tar.gz");

  var files = tar.getFiles();
  for (var i=0; i<files.length; i++) {
    alert( files[0].name() + " is in this archive" );
  }

DESCRIPTION

This is a read-only port of Archive::Tar to javascript. It allows you to fetch a tar archive off the server. Then you can read header information and contents for each file contained within the tar archive.

Since we're inside the browser, the gunzipping of the archive is already done for us. Therefore, tar.read("myfile.tar") and tar.read("myfile.tar.gz") both work seamlessly

BUGS

Doesn't work in Internet Explorer yet... only firefox.

I haven't implemented tar's weird LongLink thing. When tar encounters a filename which is longer than 255 characters, it makes the filename ././@LongLink and the actual filename can be found in the contents of the next file.

METHODS

tar.read( filepath )

Fetches a tar archive from the specified filepath on the server. Then the contents are loaded and parsed using the load() method.

tar.load( content )

Loads a tar archive from a binary string.

tar.containsFile( filename )

Returns true if the specified filename is in the tar archive.

tar.listFiles( field1, field2 )

Returns an array of hashes, that contain key/value pairs for each field specified in the argument list. You can also pass an array of fields to listFiles as its only argument.

If no arguments are passed or only 'name' exists in the list of fields passed to listFiles(), then an array of file names is returned.

  var files = tar.listFiles();                  // returns ["filename", ...]
  var files = tar.listFiles(name);              // returns ["filename", ...]
  var files = tar.listFiles('name,'linkname')   // returns [{ name: "filename", linkname: ""}, ... ]

tar.getFiles( filename1, filename2 )

Returns an array of Archive.Tar.File objects for each filename passed to getFiles(). The list of filenames can be passed as arguments, or as elements in an array passed as the only argument.

If no getFiles() is called without arguments, all files, etc... contained within the archive are returned as Tar.Archive.File objects

tar.getContent( filename )

Return the content of the given file

CONSTANTS

Archive.Tar.FILE

Regular file.

Archive.Tar.HARDLINK

Archive.Tar.SYMLINK

Hard and symbolic ("soft") links. linkname should specify target.

Archive.Tar.CHARDEV

Archive.Tar.BLOCKDEV

Character and block devices. devmajor and devminor should specify the major and minor device numbers.

Archive.Tar.DIR

Directory

Archive.Tar.FIFO

FIFO (named pipe).

Archive.Tar.SOCKET

Socket

SEE ALSO

JSAN

AUTHORS

The Archive.Tar JavaScript module is written by Kevin Jones <kevinj@cpan.org>, based on Archive::Tar by Jos Boumans <kane@cpan.org>

COPYRIGHT

Copyright 2007 Kevin Jones. Copyright 2002 Jos Boumans <kane@cpan.org>

This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License

See http://www.perl.com/perl/misc/Artistic.html

if (typeof(Archive) == 'undefined') Archive = {};

Archive.Tar = function () {
    return this;
}

Archive.Tar.FILE	= 0;
Archive.Tar.HARDLINK	= 1;
Archive.Tar.SYMLINK	= 2;
Archive.Tar.CHARDEV	= 3;
Archive.Tar.BLOCKDEV	= 4;
Archive.Tar.DIR		= 5;
Archive.Tar.FIFO	= 6;
Archive.Tar.SOCKET	= 8;

Archive.Tar.prototype.read = function (filename) {
    var request;
    request = new XMLHttpRequest();
    if (!request && window.ActiveXObject) {
	request = new ActiveXObject('Msxml2.XMLHTTP') ||
		  new ActiveXObject('Microsoft.XMLHTTP');
    }

    request.open('GET', filename, false);
    request.setRequestHeader('Accept', 'text/plain; charset=x-user-defined');
    request.send('');

    if (request.status != 200) {
	throw new Error("Error fetching " + filename + ": " + request.status);
    }

    this.load(request.responseText);
}

Archive.Tar.prototype.load = function (content) { 
    this._content = content;
    this._fnames = [];
    this._finfo = {};

    while (1) {
	var block = this._getBlocks(1);
	if (block == undefined) break;

	var header = this._getHeader(block);

	var content = this._strip(
	    this._getBlocks( Math.ceil(header.size / 512) )
	);

	this._fnames.push(header.name);
	this._finfo[header.name] = [header, content];
    }
}

Archive.Tar.prototype.containsFile = function (filename) {
    return Boolean(this._finfo[filename]);
}

Archive.Tar.prototype.listFiles = function () {
    var names = Array.prototype.isPrototypeOf(arguments[0]) ? arguments[0] : arguments;

    if (!names.length || names.length == 1 && names[0] == 'name') {
	return this._fnames;
    }

    var files = [];
    for (var i=0; i<this._fnames.length; i++) {
	files.push({});
	var fname = this._fnames[i];

	for (var j=0; j<names.length; j++) {
	    var field = names[j];
	    var value = this._finfo[fname][0][field];

	    if (typeof(value) == 'undefined')
		throw new Error(field + " is not a valid field name");

	    files[i][field] = value;
	}
    }

    return files;
}

Archive.Tar.prototype.getFiles = function () {
    if (typeof(Archive.Tar.File) == 'undefined')
	throw new Error("Archive.Tar.File is required");

    var names = Array.prototype.isPrototypeOf(arguments[0]) ? arguments[0] : arguments;
    
    if (!names || !names.length)
	names = this._fnames;

    var files = [];
    
    for (var i=0; i<names.length; i++) {
	var name = names[i];

	if (!this._finfo[name])
	    throw new Error(name + " does not exist in the tar archive");
	    
	files.push(new Archive.Tar.File(this._finfo[name][0], 
					this._finfo[name][1]
				       )
		  );
    }

    return files;
}

Archive.Tar.prototype.getContent = function (file) {
    return this._finfo[file] ? this._finfo[file][1] : undefined;
}

/**
 * private methods
 */

Archive.Tar.prototype._getBlocks = function (blocks) {
    var new_offset = 512 * blocks;
    var block = this._content.slice(0, new_offset);

    if (block.match(/^\0+$/)) {
	return undefined;
    }

    this._content = this._content.slice(new_offset, this._content.length);
    return block;
}

Archive.Tar.prototype._strip = function (str) {
    var idx = str.indexOf(String.fromCharCode(0));
    return idx < 0 ? str : str.substr(0, idx);
}

Archive.Tar.prototype._getRec = function (str, bytes) {
    // Get bytes from our current position in str, then advance pos
    str = str.substr(this._pos, bytes);
    this._pos += bytes;
    
    // Strip off NULLs
    return this._strip(str);
}

Archive.Tar.prototype._getHeader = function (block) {
    this._pos = 0;
    return {
	name     : this._getRec(block, 100),

	mode     : parseInt( '0' + this._getRec(block,8) ),
	uid      : parseInt( '0' + this._getRec(block,8) ),
	gid      : parseInt( '0' + this._getRec(block,8) ),
	size     : parseInt( '0' + this._getRec(block,12) ),
	mtime    : new Date( parseInt( '0' + this._getRec(block,12)) * 1000 ),
	chksum   : parseInt( '0' + this._getRec(block,8) ),

	type     : parseInt( '0' + this._getRec(block,1) ),
	linkname : this._getRec(block,100),

	// USTAR stuff
	magic    : this._getRec(block,6),
	version  : this._getRec(block,2),

	uname    : this._getRec(block,32),
	gname    : this._getRec(block,32),

	devmajor : parseInt( '0' + this._getRec(block,8) ),
	devminor : parseInt( '0' + this._getRec(block,8) ),

	prefix   : this._getRec(block,155)
    };
}

/*

*/