484 lines
24 KiB
Plaintext
484 lines
24 KiB
Plaintext
archive_write(3) BSD Library Functions Manual archive_write(3)
|
||
|
||
[1mNAME[0m
|
||
[1marchive_write_new[22m, [1marchive_write_set_format_cpio[22m,
|
||
[1marchive_write_set_format_pax[22m, [1marchive_write_set_format_pax_restricted[22m,
|
||
[1marchive_write_set_format_shar[22m, [1marchive_write_set_format_shar_binary[22m,
|
||
[1marchive_write_set_format_ustar[22m, [1marchive_write_get_bytes_per_block[22m,
|
||
[1marchive_write_set_bytes_per_block[22m, [1marchive_write_set_bytes_in_last_block[22m,
|
||
[1marchive_write_set_compression_bzip2[22m,
|
||
[1marchive_write_set_compression_compress[22m,
|
||
[1marchive_write_set_compression_gzip[22m, [1marchive_write_set_compression_none[22m,
|
||
[1marchive_write_set_compression_program[22m,
|
||
[1marchive_write_set_compressor_options[22m, [1marchive_write_set_format_options[22m,
|
||
[1marchive_write_set_options[22m, [1marchive_write_open[22m, [1marchive_write_open_fd[22m,
|
||
[1marchive_write_open_FILE[22m, [1marchive_write_open_filename[22m,
|
||
[1marchive_write_open_memory[22m, [1marchive_write_header[22m, [1marchive_write_data[22m,
|
||
[1marchive_write_finish_entry[22m, [1marchive_write_close[22m, [1marchive_write_finish [22m--
|
||
functions for creating archives
|
||
|
||
[1mSYNOPSIS[0m
|
||
[1m#include <archive.h>[0m
|
||
|
||
[4mstruct[24m [4marchive[24m [4m*[0m
|
||
[1marchive_write_new[22m([4mvoid[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_get_bytes_per_block[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_bytes_per_block[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mint[24m [4mbytes_per_block[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_bytes_in_last_block[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mint[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_compression_bzip2[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_compression_compress[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_compression_gzip[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_compression_none[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_compression_program[22m([4mstruct[24m [4marchive[24m [4m*[24m,
|
||
[4mconst[24m [4mchar[24m [4m*[24m [4mcmd[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_format_cpio[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_format_pax[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_format_pax_restricted[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_format_shar[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_format_shar_binary[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_format_ustar[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_format_options[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mconst[24m [4mchar[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_compressor_options[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mconst[24m [4mchar[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_set_options[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mconst[24m [4mchar[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_open[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mvoid[24m [4m*client_data[24m,
|
||
[4marchive_open_callback[24m [4m*[24m, [4marchive_write_callback[24m [4m*[24m,
|
||
[4marchive_close_callback[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_open_fd[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mint[24m [4mfd[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_open_FILE[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mFILE[24m [4m*file[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_open_filename[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mconst[24m [4mchar[24m [4m*filename[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_open_memory[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mvoid[24m [4m*buffer[24m,
|
||
[4msize_t[24m [4mbufferSize[24m, [4msize_t[24m [4m*outUsed[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_header[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mstruct[24m [4marchive_entry[24m [4m*[24m);
|
||
|
||
[4mssize_t[0m
|
||
[1marchive_write_data[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mconst[24m [4mvoid[24m [4m*[24m, [4msize_t[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_finish_entry[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_close[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[4mint[0m
|
||
[1marchive_write_finish[22m([4mstruct[24m [4marchive[24m [4m*[24m);
|
||
|
||
[1mDESCRIPTION[0m
|
||
These functions provide a complete API for creating streaming archive
|
||
files. The general process is to first create the struct archive object,
|
||
set any desired options, initialize the archive, append entries, then
|
||
close the archive and release all resources. The following summary
|
||
describes the functions in approximately the order they are ordinarily
|
||
used:
|
||
|
||
[1marchive_write_new[22m()
|
||
Allocates and initializes a struct archive object suitable for
|
||
writing a tar archive.
|
||
|
||
[1marchive_write_set_bytes_per_block[22m()
|
||
Sets the block size used for writing the archive data. Every
|
||
call to the write callback function, except possibly the last
|
||
one, will use this value for the length. The third parameter is
|
||
a boolean that specifies whether or not the final block written
|
||
will be padded to the full block size. If it is zero, the last
|
||
block will not be padded. If it is non-zero, padding will be
|
||
added both before and after compression. The default is to use a
|
||
block size of 10240 bytes and to pad the last block. Note that a
|
||
block size of zero will suppress internal blocking and cause
|
||
writes to be sent directly to the write callback as they occur.
|
||
|
||
[1marchive_write_get_bytes_per_block[22m()
|
||
Retrieve the block size to be used for writing. A value of -1
|
||
here indicates that the library should use default values. A
|
||
value of zero indicates that internal blocking is suppressed.
|
||
|
||
[1marchive_write_set_bytes_in_last_block[22m()
|
||
Sets the block size used for writing the last block. If this
|
||
value is zero, the last block will be padded to the same size as
|
||
the other blocks. Otherwise, the final block will be padded to a
|
||
multiple of this size. In particular, setting it to 1 will cause
|
||
the final block to not be padded. For compressed output, any
|
||
padding generated by this option is applied only after the com-
|
||
pression. The uncompressed data is always unpadded. The default
|
||
is to pad the last block to the full block size (note that
|
||
[1marchive_write_open_filename[22m() will set this based on the file
|
||
type). Unlike the other ``set'' functions, this function can be
|
||
called after the archive is opened.
|
||
|
||
[1marchive_write_get_bytes_in_last_block[22m()
|
||
Retrieve the currently-set value for last block size. A value of
|
||
-1 here indicates that the library should use default values.
|
||
|
||
[1marchive_write_set_format_cpio[22m(), [1marchive_write_set_format_pax[22m(),
|
||
[1marchive_write_set_format_pax_restricted[22m(),
|
||
[1marchive_write_set_format_shar[22m(),
|
||
[1marchive_write_set_format_shar_binary[22m(),
|
||
[1marchive_write_set_format_ustar[22m()
|
||
Sets the format that will be used for the archive. The library
|
||
can write POSIX octet-oriented cpio format archives, POSIX-stan-
|
||
dard ``pax interchange'' format archives, traditional ``shar''
|
||
archives, enhanced ``binary'' shar archives that store a variety
|
||
of file attributes and handle binary files, and POSIX-standard
|
||
``ustar'' archives. The pax interchange format is a backwards-
|
||
compatible tar format that adds key/value attributes to each
|
||
entry and supports arbitrary filenames, linknames, uids, sizes,
|
||
etc. ``Restricted pax interchange format'' is the library
|
||
default; this is the same as pax format, but suppresses the pax
|
||
extended header for most normal files. In most cases, this will
|
||
result in ordinary ustar archives.
|
||
|
||
[1marchive_write_set_compression_bzip2[22m(),
|
||
[1marchive_write_set_compression_compress[22m(),
|
||
[1marchive_write_set_compression_gzip[22m(),
|
||
[1marchive_write_set_compression_none[22m()
|
||
The resulting archive will be compressed as specified. Note that
|
||
the compressed output is always properly blocked.
|
||
|
||
[1marchive_write_set_compression_program[22m()
|
||
The archive will be fed into the specified compression program.
|
||
The output of that program is blocked and written to the client
|
||
write callbacks.
|
||
|
||
[1marchive_write_set_compressor_options[22m(),
|
||
[1marchive_write_set_format_options[22m(), [1marchive_write_set_options[22m()
|
||
Specifies options that will be passed to the currently-enabled
|
||
compressor and/or format writer. The argument is a comma-sepa-
|
||
rated list of individual options. Individual options have one of
|
||
the following forms:
|
||
[4moption=value[0m
|
||
The option/value pair will be provided to every module.
|
||
Modules that do not accept an option with this name will
|
||
ignore it.
|
||
[4moption[24m The option will be provided to every module with a value
|
||
of ``1''.
|
||
[4m!option[0m
|
||
The option will be provided to every module with a NULL
|
||
value.
|
||
[4mmodule:option=value[24m, [4mmodule:option[24m, [4mmodule:!option[0m
|
||
As above, but the corresponding option and value will be
|
||
provided only to modules whose name matches [4mmodule[24m.
|
||
The return value will be [1mARCHIVE_OK [22mif any module accepts the
|
||
option, or [1mARCHIVE_WARN [22mif no module accepted the option, or
|
||
[1mARCHIVE_FATAL [22mif there was a fatal error while attempting to
|
||
process the option.
|
||
|
||
The currently supported options are:
|
||
Compressor gzip
|
||
[1mcompression-level[0m
|
||
The value is interpreted as a decimal integer
|
||
specifying the gzip compression level.
|
||
Compressor xz
|
||
[1mcompression-level[0m
|
||
The value is interpreted as a decimal integer
|
||
specifying the compression level.
|
||
Format mtree
|
||
[1mcksum[22m, [1mdevice[22m, [1mflags[22m, [1mgid[22m, [1mgname[22m, [1mindent[22m, [1mlink[22m, [1mmd5[22m,
|
||
[1mmode[22m, [1mnlink[22m, [1mrmd160[22m, [1msha1[22m, [1msha256[22m, [1msha384[22m,
|
||
[1msha512[22m, [1msize[22m, [1mtime[22m, [1muid[22m, [1muname[0m
|
||
Enable a particular keyword in the mtree output.
|
||
Prefix with an exclamation mark to disable the
|
||
corresponding keyword. The default is equivalent
|
||
to ``device, flags, gid, gname, link, mode,
|
||
nlink, size, time, type, uid, uname''.
|
||
[1mall [22mEnables all of the above keywords.
|
||
[1muse-set[0m
|
||
Enables generation of [1m/set [22mlines that specify
|
||
default values for the following files and/or
|
||
directories.
|
||
[1mindent [22mXXX needs explanation XXX
|
||
|
||
[1marchive_write_open[22m()
|
||
Freeze the settings, open the archive, and prepare for writing
|
||
entries. This is the most generic form of this function, which
|
||
accepts pointers to three callback functions which will be
|
||
invoked by the compression layer to write the constructed ar-
|
||
chive.
|
||
|
||
[1marchive_write_open_fd[22m()
|
||
A convenience form of [1marchive_write_open[22m() that accepts a file
|
||
descriptor. The [1marchive_write_open_fd[22m() function is safe for use
|
||
with tape drives or other block-oriented devices.
|
||
|
||
[1marchive_write_open_FILE[22m()
|
||
A convenience form of [1marchive_write_open[22m() that accepts a [4mFILE[24m [4m*[0m
|
||
pointer. Note that [1marchive_write_open_FILE[22m() is not safe for
|
||
writing to tape drives or other devices that require correct
|
||
blocking.
|
||
|
||
[1marchive_write_open_file[22m()
|
||
A deprecated synonym for [1marchive_write_open_filename[22m().
|
||
|
||
[1marchive_write_open_filename[22m()
|
||
A convenience form of [1marchive_write_open[22m() that accepts a file-
|
||
name. A NULL argument indicates that the output should be writ-
|
||
ten to standard output; an argument of ``-'' will open a file
|
||
with that name. If you have not invoked
|
||
[1marchive_write_set_bytes_in_last_block[22m(), then
|
||
[1marchive_write_open_filename[22m() will adjust the last-block padding
|
||
depending on the file: it will enable padding when writing to
|
||
standard output or to a character or block device node, it will
|
||
disable padding otherwise. You can override this by manually
|
||
invoking [1marchive_write_set_bytes_in_last_block[22m() before calling
|
||
[1marchive_write_open[22m(). The [1marchive_write_open_filename[22m() function
|
||
is safe for use with tape drives or other block-oriented devices.
|
||
|
||
[1marchive_write_open_memory[22m()
|
||
A convenience form of [1marchive_write_open[22m() that accepts a pointer
|
||
to a block of memory that will receive the archive. The final
|
||
[4msize_t[24m [4m*[24m argument points to a variable that will be updated after
|
||
each write to reflect how much of the buffer is currently in use.
|
||
You should be careful to ensure that this variable remains allo-
|
||
cated until after the archive is closed.
|
||
|
||
[1marchive_write_header[22m()
|
||
Build and write a header using the data in the provided struct
|
||
archive_entry structure. See archive_entry(3) for information on
|
||
creating and populating struct archive_entry objects.
|
||
|
||
[1marchive_write_data[22m()
|
||
Write data corresponding to the header just written. Returns
|
||
number of bytes written or -1 on error.
|
||
|
||
[1marchive_write_finish_entry[22m()
|
||
Close out the entry just written. In particular, this writes out
|
||
the final padding required by some formats. Ordinarily, clients
|
||
never need to call this, as it is called automatically by
|
||
[1marchive_write_next_header[22m() and [1marchive_write_close[22m() as needed.
|
||
|
||
[1marchive_write_close[22m()
|
||
Complete the archive and invoke the close callback.
|
||
|
||
[1marchive_write_finish[22m()
|
||
Invokes [1marchive_write_close[22m() if it was not invoked manually,
|
||
then releases all resources. Note that this function was
|
||
declared to return [4mvoid[24m in libarchive 1.x, which made it impossi-
|
||
ble to detect errors when [1marchive_write_close[22m() was invoked
|
||
implicitly from this function. This is corrected beginning with
|
||
libarchive 2.0.
|
||
More information about the [4mstruct[24m [4marchive[24m object and the overall design
|
||
of the library can be found in the libarchive(3) overview.
|
||
|
||
[1mIMPLEMENTATION[0m
|
||
Compression support is built-in to libarchive, which uses zlib and bzlib
|
||
to handle gzip and bzip2 compression, respectively.
|
||
|
||
[1mCLIENT CALLBACKS[0m
|
||
To use this library, you will need to define and register callback func-
|
||
tions that will be invoked to write data to the resulting archive. These
|
||
functions are registered by calling [1marchive_write_open[22m():
|
||
|
||
[4mtypedef[24m [4mint[24m [1marchive_open_callback[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mvoid[0m
|
||
[4m*client_data[24m)
|
||
|
||
The open callback is invoked by [1marchive_write_open[22m(). It should return
|
||
[1mARCHIVE_OK [22mif the underlying file or data source is successfully opened.
|
||
If the open fails, it should call [1marchive_set_error[22m() to register an
|
||
error code and message and return [1mARCHIVE_FATAL[22m.
|
||
|
||
[4mtypedef[24m [4mssize_t[24m [1marchive_write_callback[22m([4mstruct[24m [4marchive[24m [4m*[24m,
|
||
[4mvoid[24m [4m*client_data[24m, [4mconst[24m [4mvoid[24m [4m*buffer[24m, [4msize_t[24m [4mlength[24m)
|
||
|
||
The write callback is invoked whenever the library needs to write raw
|
||
bytes to the archive. For correct blocking, each call to the write call-
|
||
back function should translate into a single write(2) system call. This
|
||
is especially critical when writing archives to tape drives. On success,
|
||
the write callback should return the number of bytes actually written.
|
||
On error, the callback should invoke [1marchive_set_error[22m() to register an
|
||
error code and message and return -1.
|
||
|
||
[4mtypedef[24m [4mint[24m [1marchive_close_callback[22m([4mstruct[24m [4marchive[24m [4m*[24m, [4mvoid[0m
|
||
[4m*client_data[24m)
|
||
|
||
The close callback is invoked by archive_close when the archive process-
|
||
ing is complete. The callback should return [1mARCHIVE_OK [22mon success. On
|
||
failure, the callback should invoke [1marchive_set_error[22m() to register an
|
||
error code and message and return [1mARCHIVE_FATAL.[0m
|
||
|
||
[1mEXAMPLE[0m
|
||
The following sketch illustrates basic usage of the library. In this
|
||
example, the callback functions are simply wrappers around the standard
|
||
open(2), write(2), and close(2) system calls.
|
||
|
||
#include <sys/stat.h>
|
||
#include <archive.h>
|
||
#include <archive_entry.h>
|
||
#include <fcntl.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
|
||
struct mydata {
|
||
const char *name;
|
||
int fd;
|
||
};
|
||
|
||
int
|
||
myopen(struct archive *a, void *client_data)
|
||
{
|
||
struct mydata *mydata = client_data;
|
||
|
||
mydata->fd = open(mydata->name, O_WRONLY | O_CREAT, 0644);
|
||
if (mydata->fd >= 0)
|
||
return (ARCHIVE_OK);
|
||
else
|
||
return (ARCHIVE_FATAL);
|
||
}
|
||
|
||
ssize_t
|
||
mywrite(struct archive *a, void *client_data, const void *buff, size_t n)
|
||
{
|
||
struct mydata *mydata = client_data;
|
||
|
||
return (write(mydata->fd, buff, n));
|
||
}
|
||
|
||
int
|
||
myclose(struct archive *a, void *client_data)
|
||
{
|
||
struct mydata *mydata = client_data;
|
||
|
||
if (mydata->fd > 0)
|
||
close(mydata->fd);
|
||
return (0);
|
||
}
|
||
|
||
void
|
||
write_archive(const char *outname, const char **filename)
|
||
{
|
||
struct mydata *mydata = malloc(sizeof(struct mydata));
|
||
struct archive *a;
|
||
struct archive_entry *entry;
|
||
struct stat st;
|
||
char buff[8192];
|
||
int len;
|
||
int fd;
|
||
|
||
a = archive_write_new();
|
||
mydata->name = outname;
|
||
archive_write_set_compression_gzip(a);
|
||
archive_write_set_format_ustar(a);
|
||
archive_write_open(a, mydata, myopen, mywrite, myclose);
|
||
while (*filename) {
|
||
stat(*filename, &st);
|
||
entry = archive_entry_new();
|
||
archive_entry_copy_stat(entry, &st);
|
||
archive_entry_set_pathname(entry, *filename);
|
||
archive_write_header(a, entry);
|
||
fd = open(*filename, O_RDONLY);
|
||
len = read(fd, buff, sizeof(buff));
|
||
while ( len > 0 ) {
|
||
archive_write_data(a, buff, len);
|
||
len = read(fd, buff, sizeof(buff));
|
||
}
|
||
archive_entry_free(entry);
|
||
filename++;
|
||
}
|
||
archive_write_finish(a);
|
||
}
|
||
|
||
int main(int argc, const char **argv)
|
||
{
|
||
const char *outname;
|
||
argv++;
|
||
outname = argv++;
|
||
write_archive(outname, argv);
|
||
return 0;
|
||
}
|
||
|
||
[1mRETURN VALUES[0m
|
||
Most functions return [1mARCHIVE_OK [22m(zero) on success, or one of several
|
||
non-zero error codes for errors. Specific error codes include:
|
||
[1mARCHIVE_RETRY [22mfor operations that might succeed if retried, [1mARCHIVE_WARN[0m
|
||
for unusual conditions that do not prevent further operations, and
|
||
[1mARCHIVE_FATAL [22mfor serious errors that make remaining operations impossi-
|
||
ble. The [1marchive_errno[22m() and [1marchive_error_string[22m() functions can be
|
||
used to retrieve an appropriate error code and a textual error message.
|
||
|
||
[1marchive_write_new[22m() returns a pointer to a newly-allocated struct archive
|
||
object.
|
||
|
||
[1marchive_write_data[22m() returns a count of the number of bytes actually
|
||
written. On error, -1 is returned and the [1marchive_errno[22m() and
|
||
[1marchive_error_string[22m() functions will return appropriate values. Note
|
||
that if the client-provided write callback function returns a non-zero
|
||
value, that error will be propagated back to the caller through whatever
|
||
API function resulted in that call, which may include
|
||
[1marchive_write_header[22m(), [1marchive_write_data[22m(), [1marchive_write_close[22m(), or
|
||
[1marchive_write_finish[22m(). The client callback can call [1marchive_set_error[22m()
|
||
to provide values that can then be retrieved by [1marchive_errno[22m() and
|
||
[1marchive_error_string[22m().
|
||
|
||
[1mSEE ALSO[0m
|
||
tar(1), libarchive(3), tar(5)
|
||
|
||
[1mHISTORY[0m
|
||
The [1mlibarchive [22mlibrary first appeared in FreeBSD 5.3.
|
||
|
||
[1mAUTHORS[0m
|
||
The [1mlibarchive [22mlibrary was written by Tim Kientzle <kientzle@acm.org>.
|
||
|
||
[1mBUGS[0m
|
||
There are many peculiar bugs in historic tar implementations that may
|
||
cause certain programs to reject archives written by this library. For
|
||
example, several historic implementations calculated header checksums
|
||
incorrectly and will thus reject valid archives; GNU tar does not fully
|
||
support pax interchange format; some old tar implementations required
|
||
specific field terminations.
|
||
|
||
The default pax interchange format eliminates most of the historic tar
|
||
limitations and provides a generic key/value attribute facility for ven-
|
||
dor-defined extensions. One oversight in POSIX is the failure to provide
|
||
a standard attribute for large device numbers. This library uses
|
||
``SCHILY.devminor'' and ``SCHILY.devmajor'' for device numbers that
|
||
exceed the range supported by the backwards-compatible ustar header.
|
||
These keys are compatible with Joerg Schilling's [1mstar [22marchiver. Other
|
||
implementations may not recognize these keys and will thus be unable to
|
||
correctly restore device nodes with large device numbers from archives
|
||
created by this library.
|
||
|
||
BSD May 11, 2008 BSD
|