New common unarchive code.
This commit is contained in:
parent
ecfa290cfd
commit
7ca04f328e
123
archival/ar.c
123
archival/ar.c
@ -21,41 +21,100 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* There is no signle standard to adhere to so ar may not portable
|
||||
* between different systems
|
||||
* http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <utime.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include "unarchive.h"
|
||||
#include "busybox.h"
|
||||
|
||||
static void header_verbose_list_ar(const file_header_t *file_header)
|
||||
{
|
||||
const char *mode = mode_string(file_header->mode);
|
||||
char *mtime;
|
||||
|
||||
mtime = ctime(&file_header->mtime);
|
||||
mtime[16] = ' ';
|
||||
memmove(&mtime[17], &mtime[20], 4);
|
||||
mtime[21] = '\0';
|
||||
printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid, (int) file_header->size, &mtime[4], file_header->name);
|
||||
}
|
||||
|
||||
#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO
|
||||
/* This is simpler than data_extract_all */
|
||||
static void data_extract_regular_file(archive_handle_t *archive_handle)
|
||||
{
|
||||
file_header_t *file_header;
|
||||
int dst_fd;
|
||||
|
||||
file_header = archive_handle->file_header;
|
||||
dst_fd = xopen(file_header->name, O_WRONLY | O_CREAT);
|
||||
copy_file_chunk_fd(archive_handle->src_fd, dst_fd, file_header->size);
|
||||
close(dst_fd);
|
||||
|
||||
chmod(file_header->name, file_header->mode);
|
||||
chown(file_header->name, file_header->uid, file_header->gid);
|
||||
|
||||
if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
|
||||
struct utimbuf t;
|
||||
t.actime = t.modtime = file_header->mtime;
|
||||
utime(file_header->name, &t);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern int ar_main(int argc, char **argv)
|
||||
{
|
||||
FILE *src_stream = NULL;
|
||||
char **extract_names = NULL;
|
||||
char ar_magic[8];
|
||||
int extract_function = extract_unconditional;
|
||||
archive_handle_t *archive_handle;
|
||||
int opt;
|
||||
int num_of_entries = 0;
|
||||
extern off_t archive_offset;
|
||||
|
||||
while ((opt = getopt(argc, argv, "ovtpx")) != -1) {
|
||||
#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO
|
||||
archive_handle = init_handle();
|
||||
#else
|
||||
char magic[8];
|
||||
|
||||
archive_handle = xcalloc(1, sizeof(archive_handle_t));
|
||||
archive_handle->filter = filter_accept_all;
|
||||
archive_handle->action_data = data_skip;
|
||||
archive_handle->action_header = header_skip;
|
||||
archive_handle->file_header =xmalloc(sizeof(file_header_t));
|
||||
#endif
|
||||
|
||||
while ((opt = getopt(argc, argv, "covtpxX")) != -1) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
extract_function |= extract_preserve_date;
|
||||
case 'p': /* print */
|
||||
archive_handle->action_data = data_extract_to_stdout;
|
||||
break;
|
||||
case 'v':
|
||||
extract_function |= extract_verbose_list;
|
||||
case 't': /* list contents */
|
||||
archive_handle->action_header = header_list;
|
||||
break;
|
||||
case 't':
|
||||
extract_function |= extract_list;
|
||||
case 'X':
|
||||
archive_handle->action_header = header_verbose_list_ar;
|
||||
case 'x': /* extract */
|
||||
#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO
|
||||
archive_handle->action_data = data_extract_all;
|
||||
#else
|
||||
archive_handle->action_data = data_extract_regular_file;
|
||||
#endif
|
||||
break;
|
||||
case 'p':
|
||||
extract_function |= extract_to_stdout;
|
||||
/* Modifiers */
|
||||
case 'o': /* preserve original dates */
|
||||
archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
|
||||
break;
|
||||
case 'x':
|
||||
extract_function |= extract_all_to_fs;
|
||||
case 'v': /* verbose */
|
||||
archive_handle->action_header = header_verbose_list_ar;
|
||||
break;
|
||||
default:
|
||||
show_usage();
|
||||
@ -67,24 +126,26 @@ extern int ar_main(int argc, char **argv)
|
||||
show_usage();
|
||||
}
|
||||
|
||||
src_stream = xfopen(argv[optind++], "r");
|
||||
archive_handle->src_fd = xopen(argv[optind++], O_RDONLY);
|
||||
|
||||
/* check ar magic */
|
||||
fread(ar_magic, 1, 8, src_stream);
|
||||
archive_offset = 8;
|
||||
if (strncmp(ar_magic,"!<arch>",7) != 0) {
|
||||
error_msg_and_die("invalid magic");
|
||||
}
|
||||
|
||||
/* Create a list of files to extract */
|
||||
/* TODO: This is the same as in tar, seperate function ? */
|
||||
while (optind < argc) {
|
||||
extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2));
|
||||
extract_names[num_of_entries] = xstrdup(argv[optind]);
|
||||
num_of_entries++;
|
||||
extract_names[num_of_entries] = NULL;
|
||||
archive_handle->filter = filter_accept_list;
|
||||
archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]);
|
||||
optind++;
|
||||
}
|
||||
|
||||
unarchive(src_stream, stdout, &get_header_ar, extract_function, "./", extract_names, NULL);
|
||||
#if defined CONFIG_DPKG_DEB
|
||||
unpack_ar_archive(archive_handle);
|
||||
#else
|
||||
xread_all(archive_handle->src_fd, magic, 7);
|
||||
if (strncmp(magic, "!<arch>", 7) != 0) {
|
||||
error_msg_and_die("Invalid ar magic");
|
||||
}
|
||||
archive_handle->offset += 7;
|
||||
|
||||
while (get_header_ar(archive_handle) == EXIT_SUCCESS);
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -7,10 +7,16 @@ mainmenu_option next_comment
|
||||
comment 'Archival Utilities'
|
||||
|
||||
bool 'ar' CONFIG_AR
|
||||
if [ "$CONFIG_AR" = "y" ] ; then
|
||||
bool ' Enable support for long filenames (not need for debs)' CONFIG_FEATURE_AR_LONG_FILENAMES
|
||||
fi
|
||||
bool 'bunzip2' CONFIG_BUNZIP2
|
||||
bool 'cpio' CONFIG_CPIO
|
||||
bool 'dpkg' CONFIG_DPKG
|
||||
bool 'dpkg_deb' CONFIG_DPKG_DEB
|
||||
if [ "$CONFIG_DPKG_DEB" = "y" ] ; then
|
||||
bool ' -x support only' CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
|
||||
fi
|
||||
bool 'gunzip' CONFIG_GUNZIP
|
||||
bool 'gzip' CONFIG_GZIP
|
||||
bool 'rpm2cpio' CONFIG_RPM2CPIO
|
||||
|
220
archival/cpio.c
220
archival/cpio.c
@ -31,66 +31,210 @@
|
||||
#include "unarchive.h"
|
||||
#include "busybox.h"
|
||||
|
||||
typedef struct hardlinks_s {
|
||||
file_header_t *entry;
|
||||
int inode;
|
||||
struct hardlinks_s *next;
|
||||
} hardlinks_t;
|
||||
|
||||
extern int cpio_main(int argc, char **argv)
|
||||
{
|
||||
FILE *src_stream = stdin;
|
||||
char **extract_names = NULL;
|
||||
int extract_function = 0;
|
||||
int num_of_entries = 0;
|
||||
int opt = 0;
|
||||
mode_t oldmask = 0;
|
||||
archive_handle_t *archive_handle;
|
||||
int opt;
|
||||
|
||||
/* Initialise */
|
||||
archive_handle = init_handle();
|
||||
archive_handle->src_fd = fileno(stdin);
|
||||
archive_handle->action_header = header_list;
|
||||
|
||||
while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i': // extract
|
||||
extract_function |= extract_all_to_fs;
|
||||
case 'i': /* extract */
|
||||
archive_handle->action_data = data_extract_all;
|
||||
break;
|
||||
case 'd': // create _leading_ directories
|
||||
extract_function |= extract_create_leading_dirs;
|
||||
oldmask = umask(077); /* Make make_directory act like GNU cpio */
|
||||
case 'd': /* create _leading_ directories */
|
||||
archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
|
||||
break;
|
||||
case 'm': // preserve modification time
|
||||
extract_function |= extract_preserve_date;
|
||||
case 'm': /* preserve modification time */
|
||||
archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
|
||||
break;
|
||||
case 'v': // verbosly list files
|
||||
extract_function |= extract_verbose_list;
|
||||
case 'v': /* verbosly list files */
|
||||
archive_handle->action_header = header_verbose_list;
|
||||
break;
|
||||
case 'u': // unconditional
|
||||
extract_function |= extract_unconditional;
|
||||
case 'u': /* unconditional */
|
||||
archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
|
||||
break;
|
||||
case 't': // list files
|
||||
extract_function |= extract_list;
|
||||
case 't': /* list files */
|
||||
archive_handle->action_header = header_list;
|
||||
break;
|
||||
case 'F':
|
||||
src_stream = xfopen(optarg, "r");
|
||||
archive_handle->src_fd = xopen(optarg, O_RDONLY);
|
||||
break;
|
||||
default:
|
||||
show_usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((extract_function & extract_all_to_fs) && (extract_function & extract_list)) {
|
||||
extract_function ^= extract_all_to_fs; /* If specify t, don't extract*/
|
||||
}
|
||||
|
||||
if ((extract_function & extract_all_to_fs) && (extract_function & extract_verbose_list)) {
|
||||
/* The meaning of v changes on extract */
|
||||
extract_function ^= extract_verbose_list;
|
||||
extract_function |= extract_list;
|
||||
}
|
||||
|
||||
while (optind < argc) {
|
||||
extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2));
|
||||
extract_names[num_of_entries] = xstrdup(argv[optind]);
|
||||
num_of_entries++;
|
||||
extract_names[num_of_entries] = NULL;
|
||||
archive_handle->filter = filter_accept_list;
|
||||
archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]);
|
||||
optind++;
|
||||
}
|
||||
|
||||
unarchive(src_stream, stdout, &get_header_cpio, extract_function, "./", extract_names, NULL);
|
||||
if (oldmask) {
|
||||
umask(oldmask); /* Restore umask if we changed it */
|
||||
while (1) {
|
||||
static hardlinks_t *saved_hardlinks = NULL;
|
||||
static unsigned short pending_hardlinks = 0;
|
||||
file_header_t *file_header = archive_handle->file_header;
|
||||
char cpio_header[110];
|
||||
int namesize;
|
||||
char dummy[16];
|
||||
int major, minor, nlink, inode;
|
||||
char extract_flag;
|
||||
|
||||
if (pending_hardlinks) { /* Deal with any pending hardlinks */
|
||||
hardlinks_t *tmp;
|
||||
hardlinks_t *oldtmp;
|
||||
|
||||
tmp = saved_hardlinks;
|
||||
oldtmp = NULL;
|
||||
|
||||
while (tmp) {
|
||||
error_msg_and_die("need to fix this\n");
|
||||
if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */
|
||||
file_header = tmp->entry;
|
||||
if (oldtmp) {
|
||||
oldtmp->next = tmp->next; /* Remove item from linked list */
|
||||
} else {
|
||||
saved_hardlinks = tmp->next;
|
||||
}
|
||||
free(tmp);
|
||||
continue;
|
||||
}
|
||||
oldtmp = tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
/* There can be padding before archive header */
|
||||
archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 4);
|
||||
|
||||
if (xread_all_eof(archive_handle->src_fd, cpio_header, 110) == 0) {
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
archive_handle->offset += 110;
|
||||
|
||||
if (strncmp(&cpio_header[0], "07070", 5) != 0) {
|
||||
printf("cpio header is %x-%x-%x-%x-%x\n",
|
||||
cpio_header[0],
|
||||
cpio_header[1],
|
||||
cpio_header[2],
|
||||
cpio_header[3],
|
||||
cpio_header[4]);
|
||||
error_msg_and_die("Unsupported cpio format");
|
||||
}
|
||||
|
||||
if ((cpio_header[5] != '1') && (cpio_header[5] != '2')) {
|
||||
error_msg_and_die("Unsupported cpio format, use newc or crc");
|
||||
}
|
||||
|
||||
sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
|
||||
dummy, &inode, (unsigned int*)&file_header->mode,
|
||||
(unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
|
||||
&nlink, &file_header->mtime, &file_header->size,
|
||||
dummy, &major, &minor, &namesize, dummy);
|
||||
|
||||
file_header->name = (char *) xmalloc(namesize + 1);
|
||||
xread(archive_handle->src_fd, file_header->name, namesize); /* Read in filename */
|
||||
file_header->name[namesize] = '\0';
|
||||
archive_handle->offset += namesize;
|
||||
|
||||
/* Update offset amount and skip padding before file contents */
|
||||
archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 4);
|
||||
|
||||
if (strcmp(file_header->name, "TRAILER!!!") == 0) {
|
||||
printf("%d blocks\n", (int) (archive_handle->offset % 512 ? (archive_handle->offset / 512) + 1 : archive_handle->offset / 512)); /* Always round up */
|
||||
if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
|
||||
hardlinks_t *tmp = saved_hardlinks;
|
||||
hardlinks_t *oldtmp = NULL;
|
||||
while (tmp) {
|
||||
error_msg("%s not created: cannot resolve hardlink", tmp->entry->name);
|
||||
oldtmp = tmp;
|
||||
tmp = tmp->next;
|
||||
free (oldtmp->entry->name);
|
||||
free (oldtmp->entry);
|
||||
free (oldtmp);
|
||||
}
|
||||
saved_hardlinks = NULL;
|
||||
pending_hardlinks = 0;
|
||||
}
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (S_ISLNK(file_header->mode)) {
|
||||
file_header->link_name = (char *) xmalloc(file_header->size + 1);
|
||||
xread(archive_handle->src_fd, file_header->link_name, file_header->size);
|
||||
file_header->link_name[file_header->size] = '\0';
|
||||
archive_handle->offset += file_header->size;
|
||||
file_header->size = 0; /* Stop possible seeks in future */
|
||||
}
|
||||
if (nlink > 1 && !S_ISDIR(file_header->mode)) {
|
||||
if (file_header->size == 0) { /* Put file on a linked list for later */
|
||||
hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
|
||||
new->next = saved_hardlinks;
|
||||
new->inode = inode;
|
||||
new->entry = file_header;
|
||||
saved_hardlinks = new;
|
||||
continue;
|
||||
} else { /* Found the file with data in */
|
||||
hardlinks_t *tmp = saved_hardlinks;
|
||||
pending_hardlinks = 1;
|
||||
while (tmp) {
|
||||
if (tmp->inode == inode) {
|
||||
tmp->entry->link_name = xstrdup(file_header->name);
|
||||
nlink--;
|
||||
}
|
||||
tmp = tmp->next;
|
||||
}
|
||||
if (nlink > 1) {
|
||||
error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?");
|
||||
}
|
||||
}
|
||||
}
|
||||
file_header->device = (major << 8) | minor;
|
||||
|
||||
extract_flag = FALSE;
|
||||
if (archive_handle->filter(archive_handle->accept, archive_handle->reject, file_header->name) == EXIT_SUCCESS) {
|
||||
struct stat statbuf;
|
||||
|
||||
extract_flag = TRUE;
|
||||
|
||||
/* Check if the file already exists */
|
||||
if (lstat (file_header->name, &statbuf) == 0) {
|
||||
if ((archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) || (statbuf.st_mtime < file_header->mtime)) {
|
||||
/* Remove file if flag set or its older than the file to be extracted */
|
||||
if (unlink(file_header->name) == -1) {
|
||||
perror_msg_and_die("Couldnt remove old file");
|
||||
}
|
||||
} else {
|
||||
if (! archive_handle->flags & ARCHIVE_EXTRACT_QUIET) {
|
||||
error_msg("%s not created: newer or same age file exists", file_header->name);
|
||||
}
|
||||
extract_flag = FALSE;
|
||||
}
|
||||
}
|
||||
archive_handle->action_header(file_header);
|
||||
}
|
||||
|
||||
archive_handle->action_header(file_header);
|
||||
if (extract_flag) {
|
||||
archive_handle->action_data(archive_handle);
|
||||
} else {
|
||||
data_skip(archive_handle);
|
||||
}
|
||||
archive_handle->offset += file_header->size;
|
||||
}
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
@ -13,119 +13,100 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include "unarchive.h"
|
||||
#include "busybox.h"
|
||||
|
||||
extern int dpkg_deb_main(int argc, char **argv)
|
||||
{
|
||||
char *prefix = NULL;
|
||||
char *filename = NULL;
|
||||
char *output_buffer = NULL;
|
||||
archive_handle_t *ar_archive;
|
||||
archive_handle_t *tar_gz_archive;
|
||||
int opt = 0;
|
||||
int arg_type = 0;
|
||||
int deb_extract_funct = extract_create_leading_dirs | extract_unconditional;
|
||||
#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
|
||||
const llist_t *control_tar_gz_llist = add_to_list(NULL, "control.tar.gz");
|
||||
#endif
|
||||
#ifndef CONFIG_AR
|
||||
char magic[7];
|
||||
#endif
|
||||
|
||||
const int arg_type_prefix = 1;
|
||||
const int arg_type_field = 2;
|
||||
const int arg_type_filename = 4;
|
||||
// const int arg_type_un_ar_gz = 8;
|
||||
/* a .deb file is an ar archive that contain three files,
|
||||
* data.tar.gz, control.tar.gz and debian
|
||||
*/
|
||||
|
||||
/* Setup the tar archive handle */
|
||||
tar_gz_archive = init_handle();
|
||||
|
||||
while ((opt = getopt(argc, argv, "ceftXxI")) != -1) {
|
||||
/* Setup an ar archive handle that refers to the gzip sub archive */
|
||||
ar_archive = init_handle();
|
||||
ar_archive->action_data_subarchive = get_header_tar_gz;
|
||||
ar_archive->sub_archive = tar_gz_archive;
|
||||
ar_archive->filter = filter_accept_list;
|
||||
ar_archive->accept = add_to_list(NULL, "data.tar.gz");
|
||||
|
||||
#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
|
||||
while ((opt = getopt(argc, argv, "cefXx")) != -1) {
|
||||
#else
|
||||
while ((opt = getopt(argc, argv, "x")) != -1) {
|
||||
#endif
|
||||
switch (opt) {
|
||||
#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
|
||||
case 'c':
|
||||
deb_extract_funct |= extract_data_tar_gz;
|
||||
deb_extract_funct |= extract_verbose_list;
|
||||
tar_gz_archive->action_header = header_verbose_list;
|
||||
break;
|
||||
case 'e':
|
||||
arg_type = arg_type_prefix;
|
||||
deb_extract_funct |= extract_control_tar_gz;
|
||||
deb_extract_funct |= extract_all_to_fs;
|
||||
ar_archive->accept = control_tar_gz_llist;
|
||||
tar_gz_archive->action_data = data_extract_all;
|
||||
break;
|
||||
case 'f':
|
||||
arg_type = arg_type_field;
|
||||
deb_extract_funct |= extract_control_tar_gz;
|
||||
deb_extract_funct |= extract_one_to_buffer;
|
||||
filename = xstrdup("./control");
|
||||
break;
|
||||
case 't': /* --fsys-tarfile, i just made up this short name */
|
||||
/* Integrate the functionality needed with some code from ar.c */
|
||||
error_msg_and_die("Option disabled");
|
||||
// arg_type = arg_type_un_ar_gz;
|
||||
/* Print the entire control file
|
||||
* it should accept a second argument which specifies a
|
||||
* specific field to print */
|
||||
ar_archive->accept = control_tar_gz_llist;
|
||||
tar_gz_archive->accept = add_to_list(NULL, "./control");;
|
||||
tar_gz_archive->filter = filter_accept_list;
|
||||
tar_gz_archive->action_data = data_extract_to_stdout;
|
||||
break;
|
||||
case 'X':
|
||||
arg_type = arg_type_prefix;
|
||||
deb_extract_funct |= extract_data_tar_gz;
|
||||
deb_extract_funct |= extract_all_to_fs;
|
||||
deb_extract_funct |= extract_list;
|
||||
tar_gz_archive->action_header = header_list;
|
||||
#endif
|
||||
case 'x':
|
||||
arg_type = arg_type_prefix;
|
||||
deb_extract_funct |= extract_data_tar_gz;
|
||||
deb_extract_funct |= extract_all_to_fs;
|
||||
break;
|
||||
case 'I':
|
||||
arg_type = arg_type_filename;
|
||||
deb_extract_funct |= extract_control_tar_gz;
|
||||
deb_extract_funct |= extract_one_to_buffer;
|
||||
tar_gz_archive->action_data = data_extract_all;
|
||||
break;
|
||||
default:
|
||||
show_usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == argc) {
|
||||
if (optind + 2 < argc) {
|
||||
show_usage();
|
||||
}
|
||||
|
||||
tar_gz_archive->src_fd = ar_archive->src_fd = xopen(argv[optind++], O_RDONLY);
|
||||
|
||||
/* Workout where to extract the files */
|
||||
if (arg_type == arg_type_prefix) {
|
||||
/* argument is a dir name */
|
||||
if ((optind + 1) == argc ) {
|
||||
prefix = xstrdup("./DEBIAN/");
|
||||
} else {
|
||||
prefix = (char *) xmalloc(strlen(argv[optind + 1]) + 2);
|
||||
strcpy(prefix, argv[optind + 1]);
|
||||
/* Make sure the directory has a trailing '/' */
|
||||
if (last_char_is(prefix, '/') == NULL) {
|
||||
strcat(prefix, "/");
|
||||
}
|
||||
}
|
||||
mkdir(prefix, 0777);
|
||||
}
|
||||
/* 2nd argument is a dir name */
|
||||
mkdir(argv[optind], 0777);
|
||||
chdir(argv[optind]);
|
||||
|
||||
if (arg_type == arg_type_filename) {
|
||||
if ((optind + 1) != argc) {
|
||||
filename = xstrdup(argv[optind + 1]);
|
||||
} else {
|
||||
error_msg_and_die("-I currently requires a filename to be specified");
|
||||
}
|
||||
#ifdef CONFIG_AR
|
||||
unpack_ar_archive(ar_archive);
|
||||
#else
|
||||
xread_all(ar_archive->src_fd, magic, 7);
|
||||
if (strncmp(magic, "!<arch>", 7) != 0) {
|
||||
error_msg_and_die("Invalid ar magic");
|
||||
}
|
||||
ar_archive->offset += 7;
|
||||
|
||||
output_buffer = deb_extract(argv[optind], stdout, deb_extract_funct, prefix, filename);
|
||||
while (get_header_ar(ar_archive) == EXIT_SUCCESS);
|
||||
#endif
|
||||
|
||||
if ((arg_type == arg_type_filename) && (output_buffer != NULL)) {
|
||||
puts(output_buffer);
|
||||
}
|
||||
else if (arg_type == arg_type_field) {
|
||||
char *field = NULL;
|
||||
char *name;
|
||||
char *value;
|
||||
int field_start = 0;
|
||||
|
||||
while (1) {
|
||||
field_start += read_package_field(&output_buffer[field_start], &name, &value);
|
||||
if (name == NULL) {
|
||||
break;
|
||||
}
|
||||
if (strcmp(name, argv[optind + 1]) == 0) {
|
||||
puts(value);
|
||||
}
|
||||
free(field);
|
||||
}
|
||||
}
|
||||
/* Cleanup */
|
||||
close (ar_archive->src_fd);
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,12 @@ static char *license_msg[] = {
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "busybox.h"
|
||||
#include "unarchive.h"
|
||||
|
||||
const char gunzip_to_stdout = 1;
|
||||
const char gunzip_force = 2;
|
||||
@ -100,23 +105,20 @@ extern int gunzip_main(int argc, char **argv)
|
||||
}
|
||||
|
||||
do {
|
||||
FILE *in_file, *out_file;
|
||||
struct stat stat_buf;
|
||||
const char *old_path = argv[optind];
|
||||
const char *delete_path = NULL;
|
||||
char *new_path = NULL;
|
||||
int src_fd;
|
||||
int dst_fd;
|
||||
|
||||
optind++;
|
||||
|
||||
if (old_path == NULL || strcmp(old_path, "-") == 0) {
|
||||
in_file = stdin;
|
||||
src_fd = fileno(stdin);
|
||||
flags |= gunzip_to_stdout;
|
||||
} else {
|
||||
in_file = wfopen(old_path, "r");
|
||||
if (in_file == NULL) {
|
||||
status = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
src_fd = xopen(old_path, O_RDONLY);
|
||||
|
||||
/* Get the time stamp on the input file. */
|
||||
if (stat(old_path, &stat_buf) < 0) {
|
||||
@ -125,16 +127,16 @@ extern int gunzip_main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Check that the input is sane. */
|
||||
if (isatty(fileno(in_file)) && ((flags & gunzip_force) == 0)) {
|
||||
if (isatty(src_fd) && ((flags & gunzip_force) == 0)) {
|
||||
error_msg_and_die
|
||||
("compressed data not read from terminal. Use -f to force it.");
|
||||
}
|
||||
|
||||
/* Set output filename and number */
|
||||
if (flags & gunzip_test) {
|
||||
out_file = xfopen("/dev/null", "w"); /* why does test use filenum 2 ? */
|
||||
dst_fd = xopen("/dev/null", O_WRONLY); /* why does test use filenum 2 ? */
|
||||
} else if (flags & gunzip_to_stdout) {
|
||||
out_file = stdout;
|
||||
dst_fd = fileno(stdout);
|
||||
} else {
|
||||
char *extension;
|
||||
|
||||
@ -151,7 +153,7 @@ extern int gunzip_main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Open output file */
|
||||
out_file = xfopen(new_path, "w");
|
||||
dst_fd = xopen(new_path, O_WRONLY | O_CREAT);
|
||||
|
||||
/* Set permissions on the file */
|
||||
chmod(new_path, stat_buf.st_mode);
|
||||
@ -161,16 +163,22 @@ extern int gunzip_main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* do the decompression, and cleanup */
|
||||
if ((unzip(in_file, out_file) != 0) && (new_path)) {
|
||||
check_header_gzip(src_fd);
|
||||
if (inflate(src_fd, dst_fd) != 0) {
|
||||
error_msg("Error inflating");
|
||||
}
|
||||
check_trailer_gzip(src_fd);
|
||||
|
||||
if ((status != EXIT_SUCCESS) && (new_path)) {
|
||||
/* Unzip failed, remove new path instead of old path */
|
||||
delete_path = new_path;
|
||||
}
|
||||
|
||||
if (out_file != stdout) {
|
||||
fclose(out_file);
|
||||
if (dst_fd != fileno(stdout)) {
|
||||
close(dst_fd);
|
||||
}
|
||||
if (in_file != stdin) {
|
||||
fclose(in_file);
|
||||
if (src_fd != fileno(stdin)) {
|
||||
close(src_fd);
|
||||
}
|
||||
|
||||
/* delete_path will be NULL if in test mode or from stdin */
|
||||
|
@ -22,14 +22,41 @@ ifndef $(LIBUNARCHIVE_DIR)
|
||||
LIBUNARCHIVE_DIR:=$(TOPDIR)archival/libunarchive/
|
||||
endif
|
||||
|
||||
LIBUNARCHIVE-y:=unarchive.o seek_sub_file.o
|
||||
LIBUNARCHIVE-$(CONFIG_DPKG) += deb_extract.o get_header_ar.o get_header_tar.o
|
||||
LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += deb_extract.o get_header_ar.o get_header_tar.o
|
||||
LIBUNARCHIVE-$(CONFIG_AR) += get_header_ar.o
|
||||
LIBUNARCHIVE-$(CONFIG_CPIO) += get_header_cpio.o
|
||||
LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += get_header_cpio.o
|
||||
LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o
|
||||
LIBUNARCHIVE-$(CONFIG_UNZIP) += get_header_zip.o
|
||||
LIBUNARCHIVE-y:= \
|
||||
\
|
||||
data_skip.o \
|
||||
data_extract_all.o \
|
||||
data_extract_to_stdout.o \
|
||||
\
|
||||
filter_accept_all.o \
|
||||
filter_accept_list.o \
|
||||
filter_accept_reject_list.o \
|
||||
\
|
||||
get_header_ar.o \
|
||||
get_header_tar.o \
|
||||
get_header_tar_gz.o \
|
||||
\
|
||||
header_skip.o \
|
||||
header_list.o \
|
||||
header_verbose_list.o \
|
||||
\
|
||||
add_to_list.o \
|
||||
check_header_gzip.o \
|
||||
check_trailer_gzip.o \
|
||||
copy_file_chunk_fd.o \
|
||||
data_align.o \
|
||||
init_handle.o \
|
||||
seek_sub_file.o \
|
||||
unpack_ar_archive.o \
|
||||
|
||||
LIBUNARCHIVE-$(CONFIG_DPKG) +=
|
||||
LIBUNARCHIVE-$(CONFIG_DPKG_DEB) +=
|
||||
LIBUNARCHIVE-$(CONFIG_AR) +=
|
||||
LIBUNARCHIVE-$(CONFIG_CPIO) +=
|
||||
LIBUNARCHIVE-$(CONFIG_GUNZIP) +=
|
||||
LIBUNARCHIVE-$(CONFIG_RPM2CPIO) +=
|
||||
LIBUNARCHIVE-$(CONFIG_TAR) +=
|
||||
LIBUNARCHIVE-$(CONFIG_UNZIP) +=
|
||||
|
||||
libraries-y+=$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR)
|
||||
|
||||
|
15
archival/libunarchive/add_to_list.c
Normal file
15
archival/libunarchive/add_to_list.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "unarchive.h"
|
||||
#include "libbb.h"
|
||||
|
||||
extern const llist_t *add_to_list(const llist_t *old_head, const char *new_item)
|
||||
{
|
||||
llist_t *new_head;
|
||||
|
||||
new_head = xmalloc(sizeof(llist_t));
|
||||
new_head->data = new_item;
|
||||
new_head->link = old_head;
|
||||
|
||||
return(new_head);
|
||||
}
|
75
archival/libunarchive/check_header_gzip.c
Normal file
75
archival/libunarchive/check_header_gzip.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "libbb.h"
|
||||
|
||||
extern void check_header_gzip(int src_fd)
|
||||
{
|
||||
union {
|
||||
unsigned char raw[10];
|
||||
struct {
|
||||
unsigned char magic[2];
|
||||
unsigned char method;
|
||||
unsigned char flags;
|
||||
unsigned int mtime;
|
||||
unsigned char xtra_flags;
|
||||
unsigned char os_flags;
|
||||
} formated;
|
||||
} header;
|
||||
|
||||
xread_all(src_fd, header.raw, 10);
|
||||
|
||||
/* Magic header for gzip files, 1F 8B = \037\213 */
|
||||
if ((header.formated.magic[0] != 0x1F)
|
||||
|| (header.formated.magic[1] != 0x8b)) {
|
||||
error_msg_and_die("Invalid gzip magic");
|
||||
}
|
||||
|
||||
/* Check the compression method */
|
||||
if (header.formated.method != 8) {
|
||||
error_msg_and_die("Unknown compression method %d",
|
||||
header.formated.method);
|
||||
}
|
||||
|
||||
if (header.formated.flags & 0x04) {
|
||||
/* bit 2 set: extra field present */
|
||||
unsigned char extra_short;
|
||||
|
||||
extra_short = xread_char(src_fd);
|
||||
extra_short += xread_char(src_fd) << 8;
|
||||
while (extra_short > 0) {
|
||||
/* Ignore extra field */
|
||||
xread_char(src_fd);
|
||||
extra_short--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Discard original name if any */
|
||||
if (header.formated.flags & 0x08) {
|
||||
/* bit 3 set: original file name present */
|
||||
char tmp;
|
||||
|
||||
do {
|
||||
read(src_fd, &tmp, 1);
|
||||
} while (tmp != 0);
|
||||
}
|
||||
|
||||
/* Discard file comment if any */
|
||||
if (header.formated.flags & 0x10) {
|
||||
/* bit 4 set: file comment present */
|
||||
char tmp;
|
||||
|
||||
do {
|
||||
read(src_fd, &tmp, 1);
|
||||
} while (tmp != 0);
|
||||
}
|
||||
|
||||
/* Read the header checksum */
|
||||
if (header.formated.flags & 0x02) {
|
||||
char tmp;
|
||||
|
||||
read(src_fd, &tmp, 1);
|
||||
read(src_fd, &tmp, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
62
archival/libunarchive/check_trailer_gzip.c
Normal file
62
archival/libunarchive/check_trailer_gzip.c
Normal file
@ -0,0 +1,62 @@
|
||||
/* vi: set sw=4 ts=4: */
|
||||
/*
|
||||
* busybox gunzip trailing header handler
|
||||
* Copyright Glenn McGrath 2002
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "config.h"
|
||||
#include "busybox.h"
|
||||
#include "unarchive.h"
|
||||
|
||||
extern unsigned int gunzip_crc;
|
||||
extern unsigned int gunzip_bytes_out;
|
||||
extern unsigned char *gunzip_in_buffer;
|
||||
extern unsigned char gunzip_in_buffer_count;
|
||||
|
||||
extern void check_trailer_gzip(int src_fd)
|
||||
{
|
||||
|
||||
unsigned int stored_crc = 0;
|
||||
unsigned char count;
|
||||
|
||||
/* top up the input buffer with the rest of the trailer */
|
||||
xread_all(src_fd, &gunzip_in_buffer[gunzip_in_buffer_count], 8 - gunzip_in_buffer_count);
|
||||
for (count = 0; count != 4; count++) {
|
||||
stored_crc |= (gunzip_in_buffer[count] << (count * 8));
|
||||
}
|
||||
|
||||
/* Validate decompression - crc */
|
||||
if (stored_crc != (gunzip_crc ^ 0xffffffffL)) {
|
||||
error_msg("invalid compressed data--crc error");
|
||||
}
|
||||
|
||||
/* Validate decompression - size */
|
||||
if (gunzip_bytes_out !=
|
||||
(gunzip_in_buffer[4] | (gunzip_in_buffer[5] << 8) |
|
||||
(gunzip_in_buffer[6] << 16) | (gunzip_in_buffer[7] << 24))) {
|
||||
error_msg("invalid compressed data--length error");
|
||||
}
|
||||
|
||||
}
|
33
archival/libunarchive/copy_file_chunk_fd.c
Normal file
33
archival/libunarchive/copy_file_chunk_fd.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include "libbb.h"
|
||||
|
||||
/* Copy CHUNKSIZE bytes (or untill EOF if chunksize == -1)
|
||||
* from SRC_FILE to DST_FILE. */
|
||||
extern int copy_file_chunk_fd(int src_fd, int dst_fd, off_t chunksize)
|
||||
{
|
||||
size_t nread, size;
|
||||
char buffer[BUFSIZ];
|
||||
|
||||
while (chunksize != 0) {
|
||||
if (chunksize > BUFSIZ) {
|
||||
size = BUFSIZ;
|
||||
} else {
|
||||
size = chunksize;
|
||||
}
|
||||
nread = xread(src_fd, buffer, size);
|
||||
if (nread == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (write (dst_fd, buffer, nread) != nread) {
|
||||
error_msg_and_die ("Short write");
|
||||
}
|
||||
|
||||
if (chunksize != -1) {
|
||||
chunksize -= nread;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
13
archival/libunarchive/data_align.c
Normal file
13
archival/libunarchive/data_align.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include "unarchive.h"
|
||||
#include "libbb.h"
|
||||
|
||||
extern const unsigned short data_align(const int src_fd, const unsigned int offset, const unsigned short align_to)
|
||||
{
|
||||
const unsigned short skip_amount = (align_to - (offset % align_to)) % align_to;
|
||||
seek_sub_file(src_fd, skip_amount);
|
||||
|
||||
return(skip_amount);
|
||||
}
|
76
archival/libunarchive/data_extract_all.c
Normal file
76
archival/libunarchive/data_extract_all.c
Normal file
@ -0,0 +1,76 @@
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <utime.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "libbb.h"
|
||||
#include "unarchive.h"
|
||||
|
||||
extern void data_extract_all(archive_handle_t *archive_handle)
|
||||
{
|
||||
file_header_t *file_header = archive_handle->file_header;
|
||||
int dst_fd;
|
||||
int res;
|
||||
|
||||
if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) {
|
||||
char *dir = dirname(strdup(file_header->name));
|
||||
make_directory (dir, 0777, FILEUTILS_RECUR);
|
||||
free(dir);
|
||||
}
|
||||
|
||||
/* Create the file */
|
||||
switch(file_header->mode & S_IFMT) {
|
||||
case S_IFREG: {
|
||||
#ifdef CONFIG_CPIO
|
||||
if (file_header->link_name) {
|
||||
/* hard link */
|
||||
res = link(file_header->link_name, file_header->name);
|
||||
if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
|
||||
perror_msg("Couldnt create hard link");
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* Regular file */
|
||||
dst_fd = xopen(file_header->name, O_WRONLY | O_CREAT);
|
||||
copy_file_chunk_fd(archive_handle->src_fd, dst_fd, file_header->size);
|
||||
close(dst_fd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case S_IFDIR:
|
||||
res = mkdir(file_header->name, file_header->mode);
|
||||
if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
|
||||
perror_msg("extract_archive: %s", file_header->name);
|
||||
}
|
||||
break;
|
||||
case S_IFLNK:
|
||||
/* Symlink */
|
||||
res = symlink(file_header->link_name, file_header->name);
|
||||
if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
|
||||
perror_msg("Cannot create symlink from %s to '%s'", file_header->name, file_header->link_name);
|
||||
}
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
case S_IFIFO:
|
||||
res = mknod(file_header->name, file_header->mode, file_header->device);
|
||||
if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
|
||||
perror_msg("Cannot create node %s", file_header->name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
chmod(file_header->name, file_header->mode);
|
||||
chown(file_header->name, file_header->uid, file_header->gid);
|
||||
|
||||
if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
|
||||
struct utimbuf t;
|
||||
t.actime = t.modtime = file_header->mtime;
|
||||
utime(file_header->name, &t);
|
||||
}
|
||||
}
|
8
archival/libunarchive/data_extract_to_stdout.c
Normal file
8
archival/libunarchive/data_extract_to_stdout.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "unarchive.h"
|
||||
|
||||
extern void data_extract_to_stdout(archive_handle_t *archive_handle)
|
||||
{
|
||||
copy_file_chunk_fd(archive_handle->src_fd, fileno(stdout), archive_handle->file_header->size);
|
||||
}
|
27
archival/libunarchive/data_skip.c
Normal file
27
archival/libunarchive/data_skip.c
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "unarchive.h"
|
||||
#include "libbb.h"
|
||||
|
||||
extern void data_skip(archive_handle_t *archive_handle)
|
||||
{
|
||||
seek_sub_file(archive_handle->src_fd, archive_handle->file_header->size);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
10
archival/libunarchive/filter_accept_all.c
Normal file
10
archival/libunarchive/filter_accept_all.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <fnmatch.h>
|
||||
#include <stdlib.h>
|
||||
#include "unarchive.h"
|
||||
/*
|
||||
* Accept names that are in the accept list
|
||||
*/
|
||||
extern char filter_accept_all(const llist_t *accept_list, const llist_t *reject_list, const char *key)
|
||||
{
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
16
archival/libunarchive/filter_accept_list.c
Normal file
16
archival/libunarchive/filter_accept_list.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include <fnmatch.h>
|
||||
#include <stdlib.h>
|
||||
#include "unarchive.h"
|
||||
/*
|
||||
* Accept names that are in the accept list
|
||||
*/
|
||||
extern char filter_accept_list(const llist_t *accept_list, const llist_t *reject_list, const char *key)
|
||||
{
|
||||
while (accept_list) {
|
||||
if (fnmatch(accept_list->data, key, 0) == 0) {
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
accept_list = accept_list->link;
|
||||
}
|
||||
return(EXIT_FAILURE);
|
||||
}
|
34
archival/libunarchive/filter_accept_reject_list.c
Normal file
34
archival/libunarchive/filter_accept_reject_list.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <fnmatch.h>
|
||||
#include <stdlib.h>
|
||||
#include "unarchive.h"
|
||||
|
||||
static char check_list(const llist_t *list, const char *filename)
|
||||
{
|
||||
if (list) {
|
||||
while (list) {
|
||||
if (fnmatch(list->data, filename, 0) == 0) {
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
list = list->link;
|
||||
}
|
||||
}
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Accept names that are in the accept list
|
||||
*/
|
||||
extern char filter_accept_reject_list(const llist_t *accept_list, const llist_t *reject_list, const char *key)
|
||||
{
|
||||
/* Fail if an accept list was specified and the key wasnt in there */
|
||||
if ((accept_list) && (check_list(accept_list, key) == EXIT_FAILURE)) {
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* If the key is in a reject list fail */
|
||||
if (check_list(reject_list, key) == EXIT_FAILURE) {
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
@ -17,12 +17,13 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "unarchive.h"
|
||||
#include "libbb.h"
|
||||
|
||||
file_header_t *get_header_ar(FILE *src_stream)
|
||||
extern char get_header_ar(archive_handle_t *archive_handle)
|
||||
{
|
||||
file_header_t *typed;
|
||||
file_header_t *typed = archive_handle->file_header;
|
||||
union {
|
||||
char raw[60];
|
||||
struct {
|
||||
@ -35,72 +36,87 @@ file_header_t *get_header_ar(FILE *src_stream)
|
||||
char magic[2];
|
||||
} formated;
|
||||
} ar;
|
||||
#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
|
||||
static char *ar_long_names;
|
||||
static unsigned int ar_long_name_size;
|
||||
#endif
|
||||
|
||||
if (fread(ar.raw, 1, 60, src_stream) != 60) {
|
||||
return(NULL);
|
||||
/* dont use xread as we want to handle the error ourself */
|
||||
if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
|
||||
/* End Of File */
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Some ar entries have a trailing '\n' after the previous data entry */
|
||||
if (ar.raw[0] == '\n') {
|
||||
/* fix up the header, we started reading 1 byte too early */
|
||||
memmove(ar.raw, &ar.raw[1], 59);
|
||||
ar.raw[59] = xread_char(archive_handle->src_fd);
|
||||
archive_handle->offset++;
|
||||
}
|
||||
archive_offset += 60;
|
||||
archive_handle->offset += 60;
|
||||
|
||||
/* align the headers based on the header magic */
|
||||
if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) {
|
||||
/* some version of ar, have an extra '\n' after each data entry,
|
||||
* this puts the next header out by 1 */
|
||||
if (ar.formated.magic[1] != '`') {
|
||||
error_msg("Invalid magic");
|
||||
return(NULL);
|
||||
}
|
||||
/* read the next char out of what would be the data section,
|
||||
* if its a '\n' then it is a valid header offset by 1*/
|
||||
archive_offset++;
|
||||
if (fgetc(src_stream) != '\n') {
|
||||
error_msg("Invalid magic");
|
||||
return(NULL);
|
||||
}
|
||||
/* fix up the header, we started reading 1 byte too early */
|
||||
/* raw_header[60] wont be '\n' as it should, but it doesnt matter */
|
||||
memmove(ar.raw, &ar.raw[1], 59);
|
||||
error_msg_and_die("Invalid ar header");
|
||||
}
|
||||
|
||||
typed = (file_header_t *) xcalloc(1, sizeof(file_header_t));
|
||||
|
||||
typed->size = (size_t) atoi(ar.formated.size);
|
||||
typed->mode = strtol(ar.formated.mode, NULL, 8);
|
||||
typed->mtime = atoi(ar.formated.date);
|
||||
typed->uid = atoi(ar.formated.uid);
|
||||
typed->gid = atoi(ar.formated.gid);
|
||||
typed->size = atoi(ar.formated.size);
|
||||
|
||||
/* long filenames have '/' as the first character */
|
||||
if (ar.formated.name[0] == '/') {
|
||||
#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
|
||||
if (ar.formated.name[1] == '/') {
|
||||
/* If the second char is a '/' then this entries data section
|
||||
* stores long filename for multiple entries, they are stored
|
||||
* in static variable long_names for use in future entries */
|
||||
ar_long_names = (char *) xrealloc(ar_long_names, typed->size);
|
||||
fread(ar_long_names, 1, typed->size, src_stream);
|
||||
archive_offset += typed->size;
|
||||
ar_long_name_size = typed->size;
|
||||
ar_long_names = xmalloc(ar_long_name_size);
|
||||
xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size);
|
||||
archive_handle->offset += ar_long_name_size;
|
||||
/* This ar entries data section only contained filenames for other records
|
||||
* they are stored in the static ar_long_names for future reference */
|
||||
return (get_header_ar(src_stream)); /* Return next header */
|
||||
return (get_header_ar(archive_handle)); /* Return next header */
|
||||
} else if (ar.formated.name[1] == ' ') {
|
||||
/* This is the index of symbols in the file for compilers */
|
||||
seek_sub_file(src_stream, typed->size);
|
||||
return (get_header_ar(src_stream)); /* Return next header */
|
||||
data_skip(archive_handle);
|
||||
return (get_header_ar(archive_handle)); /* Return next header */
|
||||
} else {
|
||||
/* The number after the '/' indicates the offset in the ar data section
|
||||
(saved in variable long_name) that conatains the real filename */
|
||||
if (!ar_long_names) {
|
||||
error_msg("Cannot resolve long file name");
|
||||
return (NULL);
|
||||
const unsigned int long_offset = atoi(&ar.formated.name[1]);
|
||||
if (long_offset >= ar_long_name_size) {
|
||||
error_msg_and_die("Cant resolve long filename");
|
||||
}
|
||||
typed->name = xstrdup(ar_long_names + atoi(&ar.formated.name[1]));
|
||||
typed->name = xstrdup(ar_long_names + long_offset);
|
||||
}
|
||||
#else
|
||||
error_msg_and_die("long filenames not supported");
|
||||
#endif
|
||||
} else {
|
||||
/* short filenames */
|
||||
typed->name = xstrndup(ar.formated.name, 16);
|
||||
}
|
||||
typed->name[strcspn(typed->name, " /")]='\0';
|
||||
|
||||
/* convert the rest of the now valid char header to its typed struct */
|
||||
parse_mode(ar.formated.mode, &typed->mode);
|
||||
typed->mtime = atoi(ar.formated.date);
|
||||
typed->uid = atoi(ar.formated.uid);
|
||||
typed->gid = atoi(ar.formated.gid);
|
||||
typed->name[strcspn(typed->name, " /")] = '\0';
|
||||
|
||||
return(typed);
|
||||
if (archive_handle->filter(archive_handle->accept, archive_handle->reject, typed->name) == EXIT_SUCCESS) {
|
||||
archive_handle->action_header(typed);
|
||||
if (archive_handle->sub_archive) {
|
||||
while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
|
||||
} else {
|
||||
archive_handle->action_data(archive_handle);
|
||||
}
|
||||
} else {
|
||||
data_skip(archive_handle);
|
||||
}
|
||||
|
||||
archive_handle->offset += typed->size + 1;
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,9 @@
|
||||
#include "unarchive.h"
|
||||
#include "libbb.h"
|
||||
|
||||
file_header_t *get_header_tar(FILE * tar_stream)
|
||||
extern char get_header_tar(archive_handle_t *archive_handle)
|
||||
{
|
||||
file_header_t *file_header = archive_handle->file_header;
|
||||
union {
|
||||
unsigned char raw[512];
|
||||
struct {
|
||||
@ -44,20 +45,22 @@ file_header_t *get_header_tar(FILE * tar_stream)
|
||||
char padding[12]; /* 500-512 */
|
||||
} formated;
|
||||
} tar;
|
||||
file_header_t *tar_entry = NULL;
|
||||
long sum = 0;
|
||||
long i;
|
||||
|
||||
if (archive_offset % 512 != 0) {
|
||||
seek_sub_file(tar_stream, 512 - (archive_offset % 512));
|
||||
}
|
||||
/* Align header */
|
||||
archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 512);
|
||||
|
||||
if (fread(tar.raw, 1, 512, tar_stream) != 512) {
|
||||
/* Unfortunatly its common for tar files to have all sorts of
|
||||
* trailing garbage, fail silently */
|
||||
return (NULL);
|
||||
if (xread_all_eof(archive_handle->src_fd, tar.raw, 512) == 0) {
|
||||
/* End of file */
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
archive_handle->offset += 512;
|
||||
|
||||
/* If there is no filename its an empty header */
|
||||
if (tar.formated.name[0] == 0) {
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
archive_offset += 512;
|
||||
|
||||
/* Check header has valid magic, "ustar" is for the proper tar
|
||||
* 0's are for the old tar format
|
||||
@ -66,100 +69,102 @@ file_header_t *get_header_tar(FILE * tar_stream)
|
||||
#ifdef CONFIG_FEATURE_TAR_OLD_FORMAT
|
||||
if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0)
|
||||
#endif
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* If there is no filename its an empty header, skip it */
|
||||
if (tar.formated.name[0] == 0) {
|
||||
return (NULL);
|
||||
error_msg_and_die("Invalid tar magic");
|
||||
}
|
||||
|
||||
/* Do checksum on headers */
|
||||
for (i = 0; i < 148; i++) {
|
||||
for (i = 0; i < 148 ; i++) {
|
||||
sum += tar.raw[i];
|
||||
}
|
||||
sum += ' ' * 8;
|
||||
for (i = 156; i < 512; i++) {
|
||||
for (i = 156; i < 512 ; i++) {
|
||||
sum += tar.raw[i];
|
||||
}
|
||||
if (sum != strtol(tar.formated.chksum, NULL, 8)) {
|
||||
error_msg("Invalid tar header checksum");
|
||||
return (NULL);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* convert to type'ed variables */
|
||||
tar_entry = xcalloc(1, sizeof(file_header_t));
|
||||
if (tar.formated.prefix[0] == 0) {
|
||||
tar_entry->name = xstrdup(tar.formated.name);
|
||||
file_header->name = strdup(tar.formated.name);
|
||||
} else {
|
||||
tar_entry->name =
|
||||
concat_path_file(tar.formated.prefix, tar.formated.name);
|
||||
file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name);
|
||||
}
|
||||
|
||||
tar_entry->mode = strtol(tar.formated.mode, NULL, 8);
|
||||
tar_entry->uid = strtol(tar.formated.uid, NULL, 8);
|
||||
tar_entry->gid = strtol(tar.formated.gid, NULL, 8);
|
||||
tar_entry->size = strtol(tar.formated.size, NULL, 8);
|
||||
tar_entry->mtime = strtol(tar.formated.mtime, NULL, 8);
|
||||
tar_entry->link_name =
|
||||
strlen(tar.formated.linkname) ? xstrdup(tar.formated.linkname) : NULL;
|
||||
tar_entry->device =
|
||||
(dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) +
|
||||
file_header->mode = strtol(tar.formated.mode, NULL, 8);
|
||||
file_header->uid = strtol(tar.formated.uid, NULL, 8);
|
||||
file_header->gid = strtol(tar.formated.gid, NULL, 8);
|
||||
file_header->size = strtol(tar.formated.size, NULL, 8);
|
||||
file_header->mtime = strtol(tar.formated.mtime, NULL, 8);
|
||||
file_header->link_name = (tar.formated.linkname[0] != '\0') ?
|
||||
xstrdup(tar.formated.linkname) : NULL;
|
||||
file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) +
|
||||
strtol(tar.formated.devminor, NULL, 8));
|
||||
|
||||
#if defined CONFIG_FEATURE_TAR_OLD_FORMAT || defined CONFIG_FEATURE_GNUTAR_LONG_FILENAME
|
||||
/* Fix mode, used by the old format */
|
||||
switch (tar.formated.typeflag) {
|
||||
# ifdef CONFIG_FEATURE_TAR_OLD_FORMAT
|
||||
case 0:
|
||||
tar_entry->mode |= S_IFREG;
|
||||
file_header->mode |= S_IFREG;
|
||||
break;
|
||||
case 1:
|
||||
error_msg("internal hard link not handled\n");
|
||||
error_msg("Internal hard link not supported");
|
||||
break;
|
||||
case 2:
|
||||
tar_entry->mode |= S_IFLNK;
|
||||
file_header->mode |= S_IFLNK;
|
||||
break;
|
||||
case 3:
|
||||
tar_entry->mode |= S_IFCHR;
|
||||
file_header->mode |= S_IFCHR;
|
||||
break;
|
||||
case 4:
|
||||
tar_entry->mode |= S_IFBLK;
|
||||
file_header->mode |= S_IFBLK;
|
||||
break;
|
||||
case 5:
|
||||
tar_entry->mode |= S_IFDIR;
|
||||
file_header->mode |= S_IFDIR;
|
||||
break;
|
||||
case 6:
|
||||
tar_entry->mode |= S_IFIFO;
|
||||
file_header->mode |= S_IFIFO;
|
||||
break;
|
||||
# endif
|
||||
# ifdef CONFIG_FEATURE_GNUTAR_LONG_FILENAME
|
||||
case 'L': {
|
||||
char *longname;
|
||||
|
||||
longname = xmalloc(tar_entry->size + 1);
|
||||
fread(longname, 1, tar_entry->size, tar_stream);
|
||||
archive_offset += tar_entry->size;
|
||||
longname[tar_entry->size] = '\0';
|
||||
longname = xmalloc(file_header->size + 1);
|
||||
xread_all(archive_handle->src_fd, longname, file_header->size);
|
||||
longname[file_header->size] = '\0';
|
||||
archive_handle->offset += file_header->size;
|
||||
|
||||
tar_entry = get_header_tar(tar_stream);
|
||||
tar_entry->name = longname;
|
||||
get_header_tar(archive_handle);
|
||||
file_header->name = longname;
|
||||
break;
|
||||
}
|
||||
case 'K': {
|
||||
char *longname;
|
||||
char *linkname;
|
||||
|
||||
longname = xmalloc(tar_entry->size + 1);
|
||||
fread(longname, 1, tar_entry->size, tar_stream);
|
||||
archive_offset += tar_entry->size;
|
||||
longname[tar_entry->size] = '\0';
|
||||
linkname = xmalloc(file_header->size + 1);
|
||||
xread_all(archive_handle->src_fd, linkname, file_header->size);
|
||||
linkname[file_header->size] = '\0';
|
||||
archive_handle->offset += file_header->size;
|
||||
|
||||
tar_entry = get_header_tar(tar_stream);
|
||||
tar_entry->link_name = longname;
|
||||
get_header_tar(archive_handle);
|
||||
file_header->name = linkname;
|
||||
break;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return (tar_entry);
|
||||
if (archive_handle->filter(archive_handle->accept, archive_handle->reject, archive_handle->file_header->name) == EXIT_SUCCESS) {
|
||||
archive_handle->action_header(archive_handle->file_header);
|
||||
archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
|
||||
archive_handle->action_data(archive_handle);
|
||||
} else {
|
||||
data_skip(archive_handle);
|
||||
}
|
||||
archive_handle->offset += file_header->size;
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
73
archival/libunarchive/get_header_tar_gz.c
Normal file
73
archival/libunarchive/get_header_tar_gz.c
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "libbb.h"
|
||||
#include "unarchive.h"
|
||||
|
||||
extern char get_header_tar_gz(archive_handle_t *archive_handle)
|
||||
{
|
||||
int fd_pipe[2];
|
||||
int pid;
|
||||
|
||||
check_header_gzip(archive_handle->src_fd);
|
||||
|
||||
if (pipe(fd_pipe) != 0) {
|
||||
error_msg_and_die("Can't create pipe\n");
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
error_msg_and_die("Fork failed\n");
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* child process */
|
||||
close(fd_pipe[0]); /* We don't wan't to read from the pipe */
|
||||
inflate(archive_handle->src_fd, fd_pipe[1]);
|
||||
check_trailer_gzip(archive_handle->src_fd);
|
||||
close(fd_pipe[1]); /* Send EOF */
|
||||
exit(0);
|
||||
/* notreached */
|
||||
}
|
||||
/* parent process */
|
||||
close(fd_pipe[1]); /* Don't want to write down the pipe */
|
||||
close(archive_handle->src_fd);
|
||||
|
||||
archive_handle->src_fd = fd_pipe[0];
|
||||
|
||||
while (get_header_tar(archive_handle) == EXIT_SUCCESS);
|
||||
|
||||
if (kill(pid, SIGTERM) == -1) {
|
||||
error_msg_and_die("Couldnt kill gunzip process");
|
||||
}
|
||||
|
||||
/* I dont think this is needed */
|
||||
#if 0
|
||||
if (waitpid(pid, NULL, 0) == -1) {
|
||||
error_msg("Couldnt wait ?");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Can only do one file at a time */
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
7
archival/libunarchive/header_list.c
Normal file
7
archival/libunarchive/header_list.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include "unarchive.h"
|
||||
|
||||
extern void header_list(const file_header_t *file_header)
|
||||
{
|
||||
puts(file_header->name);
|
||||
}
|
7
archival/libunarchive/header_skip.c
Normal file
7
archival/libunarchive/header_skip.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include "unarchive.h"
|
||||
|
||||
extern void header_skip(const file_header_t *file_header)
|
||||
{
|
||||
return;
|
||||
}
|
29
archival/libunarchive/header_verbose_list.c
Normal file
29
archival/libunarchive/header_verbose_list.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "libbb.h"
|
||||
#include "unarchive.h"
|
||||
|
||||
extern void header_verbose_list(const file_header_t *file_header)
|
||||
{
|
||||
struct tm *mtime = localtime(&file_header->mtime);
|
||||
|
||||
printf("%s %d/%d%10u %4u-%02u-%02u %02u:%02u:%02u %s",
|
||||
mode_string(file_header->mode),
|
||||
file_header->uid,
|
||||
file_header->gid,
|
||||
(unsigned int) file_header->size,
|
||||
1900 + mtime->tm_year,
|
||||
1 + mtime->tm_mon,
|
||||
mtime->tm_mday,
|
||||
mtime->tm_hour,
|
||||
mtime->tm_min,
|
||||
mtime->tm_sec,
|
||||
file_header->name);
|
||||
|
||||
if (file_header->link_name) {
|
||||
printf(" -> %s", file_header->link_name);
|
||||
}
|
||||
/* putchar isnt used anywhere else i dont think */
|
||||
puts("");
|
||||
}
|
18
archival/libunarchive/init_handle.c
Normal file
18
archival/libunarchive/init_handle.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <string.h>
|
||||
#include "libbb.h"
|
||||
#include "unarchive.h"
|
||||
|
||||
archive_handle_t *init_handle(void)
|
||||
{
|
||||
archive_handle_t *archive_handle;
|
||||
|
||||
/* Initialise default values */
|
||||
archive_handle = xmalloc(sizeof(archive_handle_t));
|
||||
memset(archive_handle, 0, sizeof(archive_handle_t));
|
||||
archive_handle->file_header = xmalloc(sizeof(file_header_t));
|
||||
archive_handle->action_header = header_skip;
|
||||
archive_handle->action_data = data_skip;
|
||||
archive_handle->filter = filter_accept_all;
|
||||
|
||||
return(archive_handle);
|
||||
}
|
@ -14,23 +14,19 @@
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "unarchive.h"
|
||||
#include "libbb.h"
|
||||
|
||||
off_t archive_offset;
|
||||
|
||||
void seek_sub_file(FILE *src_stream, const int count)
|
||||
extern void seek_sub_file(const int src_fd, const unsigned int amount)
|
||||
{
|
||||
/* Try to fseek as faster */
|
||||
archive_offset += count;
|
||||
if (fseek(src_stream, count, SEEK_CUR) != 0 && errno == ESPIPE) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
fgetc(src_stream);
|
||||
if ((lseek(src_fd, amount, SEEK_CUR) == -1) && (errno == ESPIPE)) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < amount; i++) {
|
||||
xread_char(src_fd);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
34
archival/libunarchive/unpack_ar_archive.c
Normal file
34
archival/libunarchive/unpack_ar_archive.c
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include "unarchive.h"
|
||||
#include "busybox.h"
|
||||
|
||||
extern void unpack_ar_archive(archive_handle_t *ar_archive)
|
||||
{
|
||||
char magic[7];
|
||||
|
||||
xread_all(ar_archive->src_fd, magic, 7);
|
||||
if (strncmp(magic, "!<arch>", 7) != 0) {
|
||||
error_msg_and_die("Invalid ar magic");
|
||||
}
|
||||
ar_archive->offset += 7;
|
||||
|
||||
while (get_header_ar(ar_archive) == EXIT_SUCCESS);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -18,10 +18,13 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "busybox.h"
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h> /* For ntohl & htonl function */
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "busybox.h"
|
||||
#include "unarchive.h"
|
||||
|
||||
#define RPM_MAGIC "\355\253\356\333"
|
||||
#define RPM_HEADER_MAGIC "\216\255\350"
|
||||
@ -45,46 +48,54 @@ struct rpm_header {
|
||||
u_int32_t size; /* Size of store (4 bytes) */
|
||||
};
|
||||
|
||||
void skip_header(FILE *rpmfile)
|
||||
void skip_header(int rpm_fd)
|
||||
{
|
||||
struct rpm_header header;
|
||||
|
||||
fread(&header, sizeof(struct rpm_header), 1, rpmfile);
|
||||
if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */
|
||||
if (header.version != 1) error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */
|
||||
xread_all(rpm_fd, &header, sizeof(struct rpm_header));
|
||||
if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) {
|
||||
error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */
|
||||
}
|
||||
if (header.version != 1) {
|
||||
error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */
|
||||
}
|
||||
header.entries = ntohl(header.entries);
|
||||
header.size = ntohl(header.size);
|
||||
fseek (rpmfile, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
|
||||
fseek (rpmfile, header.size, SEEK_CUR); /* Seek past store */
|
||||
lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
|
||||
lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */
|
||||
}
|
||||
|
||||
/* No getopt required */
|
||||
extern int rpm2cpio_main(int argc, char **argv)
|
||||
{
|
||||
struct rpm_lead lead;
|
||||
int gunzip_pid;
|
||||
FILE *rpmfile, *cpiofile;
|
||||
int rpm_fd;
|
||||
|
||||
if (argc == 1) {
|
||||
rpmfile = stdin;
|
||||
rpm_fd = fileno(stdin);
|
||||
} else {
|
||||
rpmfile = xfopen(argv[1], "r");
|
||||
/* set the buffer size */
|
||||
setvbuf(rpmfile, NULL, _IOFBF, 0x8000);
|
||||
rpm_fd = xopen(argv[1], O_RDONLY);
|
||||
}
|
||||
|
||||
xread_all(rpm_fd, &lead, sizeof(struct rpm_lead));
|
||||
if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) {
|
||||
error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
|
||||
}
|
||||
|
||||
fread (&lead, sizeof(struct rpm_lead), 1, rpmfile);
|
||||
if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
|
||||
/* Skip the signature header */
|
||||
skip_header(rpmfile);
|
||||
fseek(rpmfile, (8 - (ftell(rpmfile) % 8)) % 8, SEEK_CUR); /* Pad to 8 byte boundary */
|
||||
skip_header(rpm_fd);
|
||||
data_align(rpm_fd, lseek(rpm_fd, 0, SEEK_CUR), 8);
|
||||
|
||||
/* Skip the main header */
|
||||
skip_header(rpmfile);
|
||||
skip_header(rpm_fd);
|
||||
|
||||
cpiofile = gz_open(rpmfile, &gunzip_pid);
|
||||
check_header_gzip(rpm_fd);
|
||||
if (inflate(rpm_fd, fileno(stdout)) != 0) {
|
||||
error_msg("Error inflating");
|
||||
}
|
||||
check_trailer_gzip(rpm_fd);
|
||||
|
||||
close(rpm_fd);
|
||||
|
||||
copyfd(fileno(cpiofile), fileno(stdout));
|
||||
gz_close(gunzip_pid);
|
||||
fclose(rpmfile);
|
||||
return 0;
|
||||
}
|
||||
|
253
archival/tar.c
253
archival/tar.c
@ -115,7 +115,7 @@ struct TarBallInfo {
|
||||
tarball lives, so we can avoid trying
|
||||
to include the tarball into itself */
|
||||
int verboseFlag; /* Whether to print extra stuff or not */
|
||||
char **excludeList; /* List of files to not include */
|
||||
const llist_t *excludeList; /* List of files to not include */
|
||||
HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
|
||||
HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
|
||||
};
|
||||
@ -325,16 +325,15 @@ static inline int writeTarHeader(struct TarBallInfo *tbInfo,
|
||||
}
|
||||
|
||||
# if defined CONFIG_FEATURE_TAR_EXCLUDE
|
||||
static inline int exclude_file(char **excluded_files, const char *file)
|
||||
static inline int exclude_file(const llist_t *excluded_files, const char *file)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (excluded_files == NULL)
|
||||
if (excluded_files == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; excluded_files[i] != NULL; i++) {
|
||||
if (excluded_files[i][0] == '/') {
|
||||
if (fnmatch(excluded_files[i], file,
|
||||
while (excluded_files) {
|
||||
if (excluded_files->data[0] == '/') {
|
||||
if (fnmatch(excluded_files->data, file,
|
||||
FNM_PATHNAME | FNM_LEADING_DIR) == 0)
|
||||
return 1;
|
||||
} else {
|
||||
@ -342,11 +341,12 @@ static inline int exclude_file(char **excluded_files, const char *file)
|
||||
|
||||
for (p = file; p[0] != '\0'; p++) {
|
||||
if ((p == file || p[-1] == '/') && p[0] != '/' &&
|
||||
fnmatch(excluded_files[i], p,
|
||||
fnmatch(excluded_files->data, p,
|
||||
FNM_PATHNAME | FNM_LEADING_DIR) == 0)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
excluded_files = excluded_files->link;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -455,8 +455,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
static inline int writeTarFile(const char *tarName, int verboseFlag,
|
||||
char **argv, char **excludeList, int gzip)
|
||||
static inline int writeTarFile(const char *tarName, const int verboseFlag,
|
||||
const llist_t *include, const llist_t *exclude, const int gzip)
|
||||
{
|
||||
#ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
int gzipDataPipe[2] = { -1, -1 };
|
||||
@ -471,8 +471,9 @@ static inline int writeTarFile(const char *tarName, int verboseFlag,
|
||||
tbInfo.hlInfoHead = NULL;
|
||||
|
||||
/* Make sure there is at least one file to tar up. */
|
||||
if (*argv == NULL)
|
||||
if (include == NULL) {
|
||||
error_msg_and_die("Cowardly refusing to create an empty archive");
|
||||
}
|
||||
|
||||
/* Open the tar file for writing. */
|
||||
if (tarName == NULL) {
|
||||
@ -544,15 +545,16 @@ static inline int writeTarFile(const char *tarName, int verboseFlag,
|
||||
}
|
||||
#endif
|
||||
|
||||
tbInfo.excludeList = excludeList;
|
||||
tbInfo.excludeList = exclude;
|
||||
|
||||
/* Read the directory/files and iterate over them one at a time */
|
||||
while (*argv != NULL) {
|
||||
if (!recursive_action(*argv++, TRUE, FALSE, FALSE,
|
||||
while (include) {
|
||||
if (!recursive_action(include->data, TRUE, FALSE, FALSE,
|
||||
writeFileToTarball, writeFileToTarball,
|
||||
(void *) &tbInfo)) {
|
||||
errorFlag = TRUE;
|
||||
}
|
||||
include = include->link;
|
||||
}
|
||||
/* Write two empty blocks to the end of the archive */
|
||||
for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++) {
|
||||
@ -582,64 +584,48 @@ static inline int writeTarFile(const char *tarName, int verboseFlag,
|
||||
}
|
||||
#endif /* tar_create */
|
||||
|
||||
void append_file_to_list(const char *new_name, char ***list, int *list_count)
|
||||
#ifdef CONFIG_FEATURE_TAR_EXCLUDE
|
||||
static const llist_t *append_file_list_to_list(const char *filename, const llist_t *list)
|
||||
{
|
||||
*list = realloc(*list, sizeof(char *) * (*list_count + 2));
|
||||
(*list)[*list_count] = xstrdup(new_name);
|
||||
(*list_count)++;
|
||||
(*list)[*list_count] = NULL;
|
||||
}
|
||||
|
||||
void append_file_list_to_list(char *filename, char ***name_list,
|
||||
int *num_of_entries)
|
||||
{
|
||||
FILE *src_stream;
|
||||
char *line;
|
||||
|
||||
src_stream = xfopen(filename, "r");
|
||||
while ((line = get_line_from_file(src_stream)) != NULL) {
|
||||
FILE *src_stream = xfopen(filename, "r");
|
||||
while(1) {
|
||||
char *line = get_line_from_file(src_stream);
|
||||
if (line == NULL) {
|
||||
break;
|
||||
}
|
||||
chomp(line);
|
||||
append_file_to_list(line, name_list, num_of_entries);
|
||||
list = add_to_list(list, line);
|
||||
free(line);
|
||||
}
|
||||
fclose(src_stream);
|
||||
|
||||
return (list);
|
||||
}
|
||||
#endif
|
||||
|
||||
int tar_main(int argc, char **argv)
|
||||
{
|
||||
enum untar_funct_e {
|
||||
/* This is optional */
|
||||
untar_unzip = 1,
|
||||
/* Require one and only one of these */
|
||||
untar_list = 2,
|
||||
untar_create = 4,
|
||||
untar_extract = 8
|
||||
};
|
||||
|
||||
FILE *src_stream = NULL;
|
||||
FILE *uncompressed_stream = NULL;
|
||||
char **include_list = NULL;
|
||||
char **exclude_list = NULL;
|
||||
char *src_filename = NULL;
|
||||
char *dst_prefix = NULL;
|
||||
int opt;
|
||||
unsigned short untar_funct = 0;
|
||||
unsigned short untar_funct_required = 0;
|
||||
unsigned short extract_function = 0;
|
||||
int include_list_count = 0;
|
||||
|
||||
#ifdef CONFIG_FEATURE_TAR_EXCLUDE
|
||||
int exclude_list_count = 0;
|
||||
#endif
|
||||
#ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
int gunzip_pid;
|
||||
int gz_fd = 0;
|
||||
char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
|
||||
#endif
|
||||
archive_handle_t *tar_handle;
|
||||
int opt;
|
||||
char *base_dir = NULL;
|
||||
|
||||
#ifdef CONFIG_FEATURE_TAR_CREATE
|
||||
char *src_filename = NULL;
|
||||
unsigned char tar_create = FALSE;
|
||||
#endif
|
||||
|
||||
if (argc < 2) {
|
||||
show_usage();
|
||||
}
|
||||
|
||||
/* Initialise default values */
|
||||
tar_handle = init_handle();
|
||||
tar_handle->src_fd = fileno(stdin);
|
||||
tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS;
|
||||
|
||||
/* Prepend '-' to the first argument if required */
|
||||
if (argv[1][0] != '-') {
|
||||
char *tmp = xmalloc(strlen(argv[1]) + 2);
|
||||
@ -648,61 +634,69 @@ int tar_main(int argc, char **argv)
|
||||
strcpy(tmp + 1, argv[1]);
|
||||
argv[1] = tmp;
|
||||
}
|
||||
|
||||
while ((opt = getopt(argc, argv, "ctxT:X:C:f:Opvz")) != -1) {
|
||||
switch (opt) {
|
||||
|
||||
/* One and only one of these is required */
|
||||
#ifdef CONFIG_FEATURE_TAR_CREATE
|
||||
case 'c':
|
||||
untar_funct_required |= untar_create;
|
||||
tar_create = TRUE;
|
||||
break;
|
||||
#endif
|
||||
case 't':
|
||||
untar_funct_required |= untar_list;
|
||||
extract_function |= extract_list | extract_unconditional;
|
||||
if ((tar_handle->action_header == header_list) ||
|
||||
(tar_handle->action_header == header_verbose_list)) {
|
||||
tar_handle->action_header = header_verbose_list;
|
||||
} else {
|
||||
tar_handle->action_header = header_list;
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
untar_funct_required |= untar_extract;
|
||||
extract_function |=
|
||||
(extract_all_to_fs | extract_unconditional |
|
||||
extract_create_leading_dirs);
|
||||
tar_handle->action_data = data_extract_all;
|
||||
break;
|
||||
|
||||
/* These are optional */
|
||||
/* Exclude or Include files listed in <filename> */
|
||||
#ifdef CONFIG_FEATURE_TAR_EXCLUDE
|
||||
case 'X':
|
||||
append_file_list_to_list(optarg, &exclude_list,
|
||||
&exclude_list_count);
|
||||
tar_handle->reject =
|
||||
append_file_list_to_list(optarg, tar_handle->reject);
|
||||
break;
|
||||
#endif
|
||||
case 'T':
|
||||
/* by default a list is an include list */
|
||||
append_file_list_to_list(optarg, &include_list,
|
||||
&include_list_count);
|
||||
break;
|
||||
|
||||
case 'C': /* Change to dir <optarg> */
|
||||
/* Make sure dst_prefix ends in a '/' */
|
||||
dst_prefix = concat_path_file(optarg, "/");
|
||||
base_dir = optarg;
|
||||
break;
|
||||
case 'f': /* archive filename */
|
||||
if (strcmp(optarg, "-") == 0) {
|
||||
src_filename = NULL;
|
||||
} else {
|
||||
src_filename = xstrdup(optarg);
|
||||
}
|
||||
#ifdef CONFIG_FEATURE_TAR_CREATE
|
||||
src_filename = optarg;
|
||||
#endif
|
||||
tar_handle->src_fd = xopen(optarg, O_RDONLY);
|
||||
break;
|
||||
case 'O':
|
||||
extract_function |= extract_to_stdout;
|
||||
case 'O': /* To stdout */
|
||||
tar_handle->action_data = data_extract_to_stdout;
|
||||
break;
|
||||
case 'p':
|
||||
tar_handle->flags |= ARCHIVE_PRESERVE_DATE;
|
||||
break;
|
||||
case 'v':
|
||||
extract_function |= extract_verbose_list;
|
||||
if ((tar_handle->action_header == header_list) ||
|
||||
(tar_handle->action_header == header_verbose_list)) {
|
||||
tar_handle->action_header = header_verbose_list;
|
||||
} else {
|
||||
tar_handle->action_header = header_list;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
case 'z':
|
||||
untar_funct |= untar_unzip;
|
||||
get_header_ptr = get_header_tar_gz;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_FEATURE_TAR_BZIP2
|
||||
/* Not enabled yet */
|
||||
case 'j':
|
||||
archive_handle->archive_action = bunzip2;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@ -710,77 +704,54 @@ int tar_main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the valid arguments were passed */
|
||||
if (untar_funct_required == 0) {
|
||||
error_msg_and_die("You must specify one of the `-ctx' options");
|
||||
}
|
||||
if ((untar_funct_required != untar_create) &&
|
||||
(untar_funct_required != untar_extract) &&
|
||||
(untar_funct_required != untar_list)) {
|
||||
error_msg_and_die("You may not specify more than one `ctx' option.");
|
||||
}
|
||||
untar_funct |= untar_funct_required;
|
||||
|
||||
/* Setup an array of filenames to work with */
|
||||
/* TODO: This is the same as in ar, seperate function ? */
|
||||
while (optind < argc) {
|
||||
append_file_to_list(argv[optind], &include_list, &include_list_count);
|
||||
char absolute_path[PATH_MAX];
|
||||
|
||||
realpath(argv[optind], absolute_path);
|
||||
tar_handle->accept = add_to_list(tar_handle->accept, absolute_path);
|
||||
optind++;
|
||||
}
|
||||
if (extract_function & (extract_list | extract_all_to_fs)) {
|
||||
if (dst_prefix == NULL) {
|
||||
dst_prefix = xstrdup("./");
|
||||
}
|
||||
|
||||
/* Setup the source of the tar data */
|
||||
if (src_filename != NULL) {
|
||||
src_stream = xfopen(src_filename, "r");
|
||||
} else {
|
||||
src_stream = stdin;
|
||||
}
|
||||
#ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
/* Get a binary tree of all the tar file headers */
|
||||
if (untar_funct & untar_unzip) {
|
||||
uncompressed_stream = gz_open(src_stream, &gunzip_pid);
|
||||
#ifdef CONFIG_FEATURE_TAR_EXCLUDE
|
||||
if (tar_handle->reject) {
|
||||
tar_handle->filter = filter_accept_reject_list;
|
||||
} else
|
||||
#endif /* CONFIG_FEATURE_TAR_GZIP */
|
||||
uncompressed_stream = src_stream;
|
||||
#endif
|
||||
tar_handle->filter = filter_accept_list;
|
||||
}
|
||||
|
||||
if ((base_dir) && (chdir(base_dir))) {
|
||||
perror_msg_and_die("Couldnt chdir");
|
||||
}
|
||||
|
||||
/* extract or list archive */
|
||||
unarchive(uncompressed_stream, stdout, &get_header_tar,
|
||||
extract_function, dst_prefix, include_list, exclude_list);
|
||||
fclose(uncompressed_stream);
|
||||
}
|
||||
#ifdef CONFIG_FEATURE_TAR_CREATE
|
||||
/* create an archive */
|
||||
else if (untar_funct & untar_create) {
|
||||
if (tar_create == TRUE) {
|
||||
int verboseFlag = FALSE;
|
||||
int gzipFlag = FALSE;
|
||||
|
||||
#ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
if (untar_funct & untar_unzip)
|
||||
# ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
if (get_header_ptr == get_header_tar_gz) {
|
||||
gzipFlag = TRUE;
|
||||
|
||||
#endif /* CONFIG_FEATURE_TAR_GZIP */
|
||||
if (extract_function & extract_verbose_list)
|
||||
}
|
||||
# endif
|
||||
if (tar_handle->action_header == header_verbose_list) {
|
||||
verboseFlag = TRUE;
|
||||
|
||||
writeTarFile(src_filename, verboseFlag, include_list, exclude_list,
|
||||
gzipFlag);
|
||||
}
|
||||
#endif /* CONFIG_FEATURE_TAR_CREATE */
|
||||
|
||||
/* Cleanups */
|
||||
#ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
if (!(untar_funct & untar_create) && (untar_funct & untar_unzip)) {
|
||||
fclose(src_stream);
|
||||
close(gz_fd);
|
||||
gz_close(gunzip_pid);
|
||||
}
|
||||
#endif /* CONFIG_FEATURE_TAR_GZIP */
|
||||
#ifdef CONFIG_FEATURE_CLEAN_UP
|
||||
if (src_filename) {
|
||||
free(src_filename);
|
||||
}
|
||||
writeTarFile(src_filename, verboseFlag, tar_handle->accept,
|
||||
tar_handle->reject, gzipFlag);
|
||||
} else
|
||||
#endif
|
||||
return (EXIT_SUCCESS);
|
||||
#ifdef CONFIG_FEATURE_TAR_GZIP
|
||||
if (get_header_ptr == get_header_tar_gz) {
|
||||
get_header_tar_gz(tar_handle);
|
||||
} else
|
||||
#endif
|
||||
while (get_header_tar(tar_handle) == EXIT_SUCCESS);
|
||||
|
||||
#ifdef CONFIG_FEATURE_CLEAN_UP
|
||||
close(tar_handle->src_fd);
|
||||
#endif
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
232
archival/unzip.c
232
archival/unzip.c
@ -20,6 +20,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* For reference to format see http://www.pkware.com/support/appnote.html */
|
||||
|
||||
/* TODO Endian issues, exclude, should we accept input from stdin ? */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
@ -28,67 +32,213 @@
|
||||
#include "unarchive.h"
|
||||
#include "busybox.h"
|
||||
|
||||
#define ZIP_FILEHEADER_MAGIC 0x04034b50
|
||||
#define ZIP_CDS_MAGIC 0x02014b50
|
||||
#define ZIP_CDS_END_MAGIC 0x06054b50
|
||||
#define ZIP_DD_MAGIC 0x08074b50
|
||||
|
||||
extern unsigned int gunzip_crc;
|
||||
extern unsigned int gunzip_bytes_out;
|
||||
|
||||
static void header_list_unzip(const file_header_t *file_header)
|
||||
{
|
||||
printf(" inflating: %s\n", file_header->name);
|
||||
}
|
||||
|
||||
static void header_verbose_list_unzip(const file_header_t *file_header)
|
||||
{
|
||||
unsigned int dostime = (unsigned int) file_header->mtime;
|
||||
|
||||
/* can printf arguments cut of the decade component ? */
|
||||
unsigned short year = 1980 + ((dostime & 0xfe000000) >> 25);
|
||||
while (year >= 100) {
|
||||
year -= 100;
|
||||
}
|
||||
|
||||
printf("%9u %02u-%02u-%02u %02u:%02u %s\n",
|
||||
(unsigned int) file_header->size,
|
||||
(dostime & 0x01e00000) >> 21,
|
||||
(dostime & 0x001f0000) >> 16,
|
||||
year,
|
||||
(dostime & 0x0000f800) >> 11,
|
||||
(dostime & 0x000007e0) >> 5,
|
||||
file_header->name);
|
||||
}
|
||||
|
||||
extern int unzip_main(int argc, char **argv)
|
||||
{
|
||||
FILE *src_stream;
|
||||
int extract_function = extract_all_to_fs | extract_create_leading_dirs;
|
||||
char **extract_names = NULL;
|
||||
char **exclude_names = NULL;
|
||||
int opt = 0;
|
||||
int num_of_entries = 0;
|
||||
int exclude = 0;
|
||||
char *outdir = "./";
|
||||
FILE *msgout = stdout;
|
||||
union {
|
||||
unsigned char raw[26];
|
||||
struct {
|
||||
unsigned short version; /* 0-1 */
|
||||
unsigned short flags; /* 2-3 */
|
||||
unsigned short method; /* 4-5 */
|
||||
unsigned short modtime; /* 6-7 */
|
||||
unsigned short moddate; /* 8-9 */
|
||||
unsigned int crc32 __attribute__ ((packed)); /* 10-13 */
|
||||
unsigned int cmpsize __attribute__ ((packed));; /* 14-17 */
|
||||
unsigned int ucmpsize __attribute__ ((packed));; /* 18-21 */
|
||||
unsigned short filename_len; /* 22-23 */
|
||||
unsigned short extra_len; /* 24-25 */
|
||||
} formated __attribute__ ((packed));
|
||||
} zip_header;
|
||||
|
||||
while ((opt = getopt(argc, argv, "lnopqxd:")) != -1) {
|
||||
archive_handle_t *archive_handle;
|
||||
unsigned int total_size = 0;
|
||||
unsigned int total_entries = 0;
|
||||
char *base_dir = NULL;
|
||||
int opt = 0;
|
||||
|
||||
/* Initialise */
|
||||
archive_handle = init_handle();
|
||||
archive_handle->action_data = NULL;
|
||||
archive_handle->action_header = header_list_unzip;
|
||||
|
||||
while ((opt = getopt(argc, argv, "lnopqd:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'l':
|
||||
extract_function |= extract_verbose_list;
|
||||
extract_function ^= extract_all_to_fs;
|
||||
case 'l': /* list */
|
||||
archive_handle->action_header = header_verbose_list_unzip;
|
||||
archive_handle->action_data = data_skip;
|
||||
break;
|
||||
case 'n':
|
||||
case 'n': /* never overwright existing files */
|
||||
break;
|
||||
case 'o':
|
||||
extract_function |= extract_unconditional;
|
||||
archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL;
|
||||
break;
|
||||
case 'p':
|
||||
extract_function |= extract_to_stdout;
|
||||
extract_function ^= extract_all_to_fs;
|
||||
/* FALLTHROUGH */
|
||||
case 'q':
|
||||
msgout = xfopen("/dev/null", "w");
|
||||
case 'p': /* extract files to stdout */
|
||||
archive_handle->action_data = data_extract_to_stdout;
|
||||
break;
|
||||
case 'd':
|
||||
outdir = xstrdup(optarg);
|
||||
strcat(outdir, "/");
|
||||
case 'q': /* Extract files quietly */
|
||||
archive_handle->action_header = header_skip;
|
||||
break;
|
||||
case 'x':
|
||||
exclude = 1;
|
||||
case 'd': /* Extract files to specified base directory*/
|
||||
base_dir = optarg;
|
||||
break;
|
||||
#if 0
|
||||
case 'x': /* Exclude the specified files */
|
||||
archive_handle->filter = filter_accept_reject_list;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
show_usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == argc) {
|
||||
if (argc == optind) {
|
||||
show_usage();
|
||||
}
|
||||
|
||||
if (*argv[optind] == '-') src_stream = stdin;
|
||||
else src_stream = xfopen(argv[optind++], "r");
|
||||
printf("Archive: %s\n", argv[optind]);
|
||||
if (archive_handle->action_header == header_verbose_list_unzip) {
|
||||
printf(" Length Date Time Name\n");
|
||||
printf(" -------- ---- ---- ----\n");
|
||||
}
|
||||
|
||||
if (*argv[optind] == '-') {
|
||||
archive_handle->src_fd = fileno(stdin);
|
||||
} else {
|
||||
archive_handle->src_fd = xopen(argv[optind++], O_RDONLY);
|
||||
}
|
||||
|
||||
if ((base_dir) && (chdir(base_dir))) {
|
||||
perror_msg_and_die("Couldnt chdir");
|
||||
}
|
||||
|
||||
while (optind < argc) {
|
||||
if (exclude) {
|
||||
exclude_names = xrealloc(exclude_names, sizeof(char *) * (num_of_entries + 2));
|
||||
exclude_names[num_of_entries] = xstrdup(argv[optind]);
|
||||
} else {
|
||||
extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2));
|
||||
extract_names[num_of_entries] = xstrdup(argv[optind]);
|
||||
}
|
||||
num_of_entries++;
|
||||
if (exclude) exclude_names[num_of_entries] = NULL;
|
||||
else extract_names[num_of_entries] = NULL;
|
||||
archive_handle->filter = filter_accept_list;
|
||||
archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]);
|
||||
optind++;
|
||||
}
|
||||
|
||||
unarchive(src_stream, msgout, &get_header_zip, extract_function, outdir, extract_names, exclude_names);
|
||||
return EXIT_SUCCESS;
|
||||
while (1) {
|
||||
unsigned int magic;
|
||||
int dst_fd;
|
||||
|
||||
/* TODO Endian issues */
|
||||
xread_all(archive_handle->src_fd, &magic, 4);
|
||||
archive_handle->offset += 4;
|
||||
|
||||
if (magic == ZIP_CDS_MAGIC) {
|
||||
break;
|
||||
}
|
||||
else if (magic != ZIP_FILEHEADER_MAGIC) {
|
||||
error_msg_and_die("Invlaide zip magic");
|
||||
}
|
||||
|
||||
/* Read the file header */
|
||||
xread_all(archive_handle->src_fd, zip_header.raw, 26);
|
||||
archive_handle->offset += 26;
|
||||
archive_handle->file_header->mode = S_IFREG | 0777;
|
||||
|
||||
if (zip_header.formated.method != 8) {
|
||||
error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method);
|
||||
}
|
||||
|
||||
/* Read filename */
|
||||
archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1);
|
||||
xread_all(archive_handle->src_fd, archive_handle->file_header->name, zip_header.formated.filename_len);
|
||||
archive_handle->offset += zip_header.formated.filename_len;
|
||||
archive_handle->file_header->name[zip_header.formated.filename_len] = '\0';
|
||||
|
||||
/* Skip extra header bits */
|
||||
archive_handle->file_header->size = zip_header.formated.extra_len;
|
||||
data_skip(archive_handle);
|
||||
archive_handle->offset += zip_header.formated.extra_len;
|
||||
|
||||
/* Handle directories */
|
||||
archive_handle->file_header->mode = S_IFREG | 0777;
|
||||
if (last_char_is(archive_handle->file_header->name, '/')) {
|
||||
archive_handle->file_header->mode ^= S_IFREG;
|
||||
archive_handle->file_header->mode |= S_IFDIR;
|
||||
}
|
||||
|
||||
/* Data section */
|
||||
archive_handle->file_header->size = zip_header.formated.cmpsize;
|
||||
if (archive_handle->action_data) {
|
||||
archive_handle->action_data(archive_handle);
|
||||
} else {
|
||||
dst_fd = xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT);
|
||||
inflate(archive_handle->src_fd, dst_fd);
|
||||
close(dst_fd);
|
||||
chmod(archive_handle->file_header->name, archive_handle->file_header->mode);
|
||||
|
||||
/* Validate decompression - crc */
|
||||
if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) {
|
||||
error_msg("Invalid compressed data--crc error");
|
||||
}
|
||||
|
||||
/* Validate decompression - size */
|
||||
if (gunzip_bytes_out != zip_header.formated.ucmpsize) {
|
||||
error_msg("Invalid compressed data--length error");
|
||||
}
|
||||
}
|
||||
|
||||
/* local file descriptor section */
|
||||
archive_handle->offset += zip_header.formated.cmpsize;
|
||||
/* This ISNT unix time */
|
||||
archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16);
|
||||
archive_handle->file_header->size = zip_header.formated.ucmpsize;
|
||||
total_size += archive_handle->file_header->size;
|
||||
total_entries++;
|
||||
|
||||
archive_handle->action_header(archive_handle->file_header);
|
||||
|
||||
/* Data descriptor section */
|
||||
if (zip_header.formated.flags & 4) {
|
||||
/* skip over duplicate crc, compressed size and uncompressed size */
|
||||
unsigned short i;
|
||||
for (i = 0; i != 12; i++) {
|
||||
xread_char(archive_handle->src_fd);
|
||||
}
|
||||
archive_handle->offset += 12;
|
||||
}
|
||||
}
|
||||
/* Central directory section */
|
||||
|
||||
if (archive_handle->action_header == header_verbose_list_unzip) {
|
||||
printf(" -------- -------\n");
|
||||
printf("%9d %d files\n", total_size, total_entries);
|
||||
}
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -48,10 +48,6 @@
|
||||
# include "sha1.h"
|
||||
#endif
|
||||
|
||||
/* Compatability with ANSI C */
|
||||
#ifndef inline
|
||||
# define inline
|
||||
#endif
|
||||
|
||||
#if (__GNU_LIBRARY__ < 5) && (!defined __dietlibc__)
|
||||
/* libc5 doesn't define socklen_t */
|
||||
@ -74,6 +70,9 @@ char *strtok_r(char *s, const char *delim, char **ptrptr);
|
||||
#define BUF_SIZE 8192
|
||||
#define EXPAND_ALLOC 1024
|
||||
|
||||
static inline int is_decimal(int ch) { return ((ch >= '0') && (ch <= '9')); }
|
||||
static inline int is_octal(int ch) { return ((ch >= '0') && (ch <= '7')); }
|
||||
|
||||
/* Macros for min/max. */
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
@ -83,8 +82,6 @@ char *strtok_r(char *s, const char *delim, char **ptrptr);
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
extern void show_usage(void) __attribute__ ((noreturn));
|
||||
extern void error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
extern void error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
|
||||
@ -228,10 +225,7 @@ extern long arith (const char *startbuf, int *errcode);
|
||||
int read_package_field(const char *package_buffer, char **field_name, char **field_value);
|
||||
char *fgets_str(FILE *file, const char *terminating_string);
|
||||
|
||||
extern int inflate(FILE *in, FILE *out);
|
||||
extern int unzip(FILE *l_in_file, FILE *l_out_file);
|
||||
extern void gz_close(int gunzip_pid);
|
||||
extern FILE *gz_open(FILE *compressed_file, int *pid);
|
||||
extern int inflate(int in, int out);
|
||||
|
||||
extern struct hostent *xgethostbyname(const char *name);
|
||||
extern struct hostent *xgethostbyname2(const char *name, int af);
|
||||
@ -335,4 +329,11 @@ extern char *pw_encrypt(const char *clear, const char *salt);
|
||||
extern struct spwd *pwd_to_spwd(const struct passwd *pw);
|
||||
extern int obscure(const char *old, const char *newval, const struct passwd *pwdp);
|
||||
|
||||
//extern int xopen(const char *pathname, int flags, mode_t mode);
|
||||
extern int xopen(const char *pathname, int flags);
|
||||
extern ssize_t xread(int fd, void *buf, size_t count);
|
||||
extern ssize_t xread_all_eof(int fd, void *buf, size_t count);
|
||||
extern void xread_all(int fd, void *buf, size_t count);
|
||||
extern unsigned char xread_char(int fd);
|
||||
|
||||
#endif /* __LIBCONFIG_H__ */
|
||||
|
@ -1,21 +1,19 @@
|
||||
#include <stdio.h> /* for FILE */
|
||||
#include <unistd.h> /* for off_t */
|
||||
#ifndef __UNARCHIVE_H__
|
||||
#define __UNARCHIVE_H__
|
||||
|
||||
enum extract_functions_e {
|
||||
extract_verbose_list = 1,
|
||||
extract_list = 2,
|
||||
extract_one_to_buffer = 4,
|
||||
extract_to_stdout = 8,
|
||||
extract_all_to_fs = 16,
|
||||
extract_preserve_date = 32,
|
||||
extract_data_tar_gz = 64,
|
||||
extract_control_tar_gz = 128,
|
||||
extract_unzip_only = 256,
|
||||
extract_unconditional = 512,
|
||||
extract_create_leading_dirs = 1024,
|
||||
extract_quiet = 2048,
|
||||
extract_exclude_list = 4096
|
||||
};
|
||||
#define ARCHIVE_PRESERVE_DATE 1
|
||||
#define ARCHIVE_CREATE_LEADING_DIRS 2
|
||||
#define ARCHIVE_EXTRACT_UNCONDITIONAL 4
|
||||
#define ARCHIVE_EXTRACT_QUIET 8
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct gunzip_s {
|
||||
unsigned short buffer_count;
|
||||
unsigned char *buffer;
|
||||
unsigned int crc;
|
||||
unsigned int count;
|
||||
} gunzip_t;
|
||||
|
||||
typedef struct file_headers_s {
|
||||
char *name;
|
||||
@ -26,23 +24,70 @@ typedef struct file_headers_s {
|
||||
mode_t mode;
|
||||
time_t mtime;
|
||||
dev_t device;
|
||||
int (*extract_func) (FILE *, FILE *);
|
||||
} file_header_t;
|
||||
|
||||
file_header_t *get_header_ar(FILE * in_file);
|
||||
file_header_t *get_header_cpio(FILE * src_stream);
|
||||
file_header_t *get_header_tar(FILE * tar_stream);
|
||||
file_header_t *get_header_zip(FILE * zip_stream);
|
||||
typedef struct llist_s {
|
||||
const char *data;
|
||||
const struct llist_s *link;
|
||||
} llist_t;
|
||||
|
||||
void seek_sub_file(FILE * src_stream, const int count);
|
||||
typedef struct archive_handle_s {
|
||||
/* define if the header and data compenent should processed */
|
||||
char (*filter)(const llist_t *, const llist_t *, const char *);
|
||||
const llist_t *accept;
|
||||
const llist_t *reject;
|
||||
|
||||
extern off_t archive_offset;
|
||||
/* Contains the processed header entry */
|
||||
file_header_t *file_header;
|
||||
|
||||
char *unarchive(FILE * src_stream, FILE * out_stream,
|
||||
file_header_t * (*get_headers) (FILE *),
|
||||
const int extract_function, const char *prefix,
|
||||
char **include_name, char **exclude_name);
|
||||
/* process the header component, e.g. tar -t */
|
||||
void (*action_header)(const file_header_t *);
|
||||
|
||||
char *deb_extract(const char *package_filename, FILE * out_stream,
|
||||
const int extract_function, const char *prefix,
|
||||
const char *filename);
|
||||
/* process the data componenet, e.g. extract to filesystem */
|
||||
void (*action_data)(struct archive_handle_s *);
|
||||
char (*action_data_subarchive)(struct archive_handle_s *);
|
||||
|
||||
/* Contains the handle to a sub archive */
|
||||
struct archive_handle_s *sub_archive;
|
||||
|
||||
/* The raw stream as read from disk or stdin */
|
||||
int src_fd;
|
||||
|
||||
/* Count the number of bytes processed */
|
||||
off_t offset;
|
||||
|
||||
/* Misc. stuff */
|
||||
unsigned char flags;
|
||||
|
||||
} archive_handle_t;
|
||||
|
||||
extern archive_handle_t *init_handle(void);
|
||||
|
||||
extern char filter_accept_all(const llist_t *accept_list, const llist_t *reject_list, const char *key);
|
||||
extern char filter_accept_list(const llist_t *accept_list, const llist_t *reject_list, const char *key);
|
||||
extern char filter_accept_reject_list(const llist_t *accept_list, const llist_t *reject_list, const char *key);
|
||||
|
||||
extern void unpack_ar_archive(archive_handle_t *ar_archive);
|
||||
|
||||
extern void data_gunzip(archive_handle_t *archive_handle);
|
||||
extern void data_skip(archive_handle_t *archive_handle);
|
||||
extern void data_extract_all(archive_handle_t *archive_handle);
|
||||
extern void data_extract_to_stdout(archive_handle_t *archive_handle);
|
||||
|
||||
extern void header_skip(const file_header_t *file_header);
|
||||
extern void header_list(const file_header_t *file_header);
|
||||
extern void header_verbose_list(const file_header_t *file_header);
|
||||
|
||||
extern void check_header_gzip(int src_fd);
|
||||
extern void check_trailer_gzip(int src_fd);
|
||||
|
||||
extern char get_header_ar(archive_handle_t *archive_handle);
|
||||
extern char get_header_tar(archive_handle_t *archive_handle);
|
||||
extern char get_header_tar_gz(archive_handle_t *archive_handle);
|
||||
|
||||
//extern void seek_sub_file(int src_fd, unsigned int amount);
|
||||
extern const unsigned short data_align(const int src_fd, const unsigned int offset, const unsigned short align_to);
|
||||
extern const llist_t *add_to_list(const llist_t *old_head, const char *new_item);
|
||||
extern int copy_file_chunk_fd(int src_fd, int dst_fd, unsigned long long chunksize);
|
||||
|
||||
#endif
|
||||
|
@ -28,20 +28,22 @@ LIBBB_SRC:= \
|
||||
copy_file_chunk.c dump.c libc5.c device_open.c error_msg.c \
|
||||
error_msg_and_die.c fgets_str.c find_mount_point.c find_pid_by_name.c \
|
||||
find_root_device.c full_read.c full_write.c get_console.c \
|
||||
get_last_path_component.c get_line_from_file.c gz_open.c human_readable.c \
|
||||
isdirectory.c kernel_version.c loop.c mode_string.c module_syscalls.c mtab.c \
|
||||
mtab_file.c my_getgrnam.c my_getgrgid.c my_getpwnam.c my_getpwnamegid.c \
|
||||
my_getpwuid.c parse_mode.c parse_number.c perror_msg.c perror_msg_and_die.c \
|
||||
print_file.c process_escape_sequence.c read_package_field.c recursive_action.c \
|
||||
safe_read.c safe_strncpy.c syscalls.c syslog_msg_with_name.c time_string.c \
|
||||
trim.c unzip.c uncompress.c vdprintf.c verror_msg.c vperror_msg.c wfopen.c \
|
||||
xgetcwd.c xreadlink.c xregcomp.c interface.c remove_file.c last_char_is.c \
|
||||
copyfd.c vherror_msg.c herror_msg.c herror_msg_and_die.c xgethostbyname.c \
|
||||
dirname.c make_directory.c create_icmp_socket.c u_signal_names.c arith.c \
|
||||
simplify_path.c inet_common.c inode_hash.c obscure.c pwd2spwd.c xfuncs.c \
|
||||
correct_password.c change_identity.c setup_environment.c run_shell.c \
|
||||
pw_encrypt.c restricted_shell.c xgethostbyname2.c create_icmp6_socket.c \
|
||||
xconnect.c bb_asprintf.c
|
||||
get_last_path_component.c get_line_from_file.c \
|
||||
human_readable.c isdirectory.c kernel_version.c loop.c \
|
||||
mode_string.c module_syscalls.c mtab.c mtab_file.c my_getgrnam.c \
|
||||
my_getgrgid.c my_getpwnam.c my_getpwnamegid.c my_getpwuid.c \
|
||||
parse_mode.c parse_number.c perror_msg.c perror_msg_and_die.c \
|
||||
print_file.c process_escape_sequence.c read_package_field.c \
|
||||
recursive_action.c safe_read.c safe_strncpy.c syscalls.c \
|
||||
syslog_msg_with_name.c time_string.c trim.c unzip.c uncompress.c \
|
||||
vdprintf.c verror_msg.c vperror_msg.c wfopen.c xgetcwd.c xreadlink.c \
|
||||
xregcomp.c interface.c remove_file.c last_char_is.c copyfd.c \
|
||||
vherror_msg.c herror_msg.c herror_msg_and_die.c xgethostbyname.c \
|
||||
dirname.c make_directory.c create_icmp_socket.c u_signal_names.c \
|
||||
arith.c simplify_path.c inet_common.c inode_hash.c obscure.c \
|
||||
pwd2spwd.c xfuncs.c correct_password.c change_identity.c \
|
||||
setup_environment.c run_shell.c pw_encrypt.c restricted_shell.c \
|
||||
xgethostbyname2.c create_icmp6_socket.c xconnect.c bb_asprintf.c
|
||||
|
||||
LIBBB_OBJS=$(patsubst %.c,$(LIBBB_DIR)%.o, $(LIBBB_SRC))
|
||||
|
||||
|
751
libbb/unzip.c
751
libbb/unzip.c
File diff suppressed because it is too large
Load Diff
@ -19,10 +19,13 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "libbb.h"
|
||||
|
||||
|
||||
@ -85,6 +88,59 @@ FILE *xfopen(const char *path, const char *mode)
|
||||
return fp;
|
||||
}
|
||||
|
||||
extern int xopen(const char *pathname, int flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = open(pathname, flags);
|
||||
if (ret == -1) {
|
||||
perror_msg_and_die("%s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern ssize_t xread(int fd, void *buf, size_t count)
|
||||
{
|
||||
ssize_t size;
|
||||
|
||||
size = read(fd, buf, count);
|
||||
if (size == -1) {
|
||||
perror_msg_and_die("Read error");
|
||||
}
|
||||
return(size);
|
||||
}
|
||||
|
||||
extern void xread_all(int fd, void *buf, size_t count)
|
||||
{
|
||||
ssize_t size;
|
||||
|
||||
size = xread(fd, buf, count);
|
||||
if (size != count) {
|
||||
error_msg_and_die("Short read");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
extern ssize_t xread_all_eof(int fd, void *buf, size_t count)
|
||||
{
|
||||
ssize_t size;
|
||||
|
||||
size = xread(fd, buf, count);
|
||||
if ((size != 0) && (size != count)) {
|
||||
error_msg_and_die("Short read");
|
||||
}
|
||||
return(size);
|
||||
}
|
||||
|
||||
extern unsigned char xread_char(int fd)
|
||||
{
|
||||
char tmp;
|
||||
|
||||
xread_all(fd, &tmp, 1);
|
||||
|
||||
return(tmp);
|
||||
}
|
||||
|
||||
/* Stupid gcc always includes its own builtin strlen()... */
|
||||
#undef strlen
|
||||
size_t xstrlen(const char *string)
|
||||
|
Loading…
Reference in New Issue
Block a user