From 9aff9036035fbed074e8e711b96c5c934e668884 Mon Sep 17 00:00:00 2001
From: Glenn L McGrath <bug1@ihug.co.nz>
Date: Wed, 13 Jun 2001 07:26:39 +0000
Subject: [PATCH] Reorganise archive extraction code

---
 Makefile               |  28 +++++------
 ar.c                   |  88 ++++++++++------------------------
 archival/ar.c          |  88 ++++++++++------------------------
 archival/dpkg.c        |  25 +++++-----
 archival/dpkg_deb.c    | 105 +++++++++++++++++++++++------------------
 dpkg.c                 |  25 +++++-----
 dpkg_deb.c             | 105 +++++++++++++++++++++++------------------
 include/libbb.h        |  49 ++++++++++---------
 libbb/deb_extract.c    |  96 +++++++++++++++++++++++--------------
 libbb/get_ar_headers.c |  15 ++----
 libbb/libbb.h          |  49 ++++++++++---------
 11 files changed, 330 insertions(+), 343 deletions(-)

diff --git a/Makefile b/Makefile
index 2af152516..f8e60443e 100644
--- a/Makefile
+++ b/Makefile
@@ -236,21 +236,21 @@ endif
     
 LIBBB	  = libbb
 LIBBB_LIB = libbb.a
-LIBBB_CSRC= ask_confirmation.c chomp.c \
-concat_path_file.c copy_file.c copy_file_chunk.c create_path.c \
+LIBBB_CSRC= append_archive_list.c add_from_archive_list.c ask_confirmation.c \
+chomp.c concat_path_file.c copy_file.c copy_file_chunk.c create_path.c \
 daemon.c deb_extract.c device_open.c error_msg.c error_msg_and_die.c \
-find_mount_point.c find_pid_by_name.c find_root_device.c full_read.c \
-full_write.c get_ar_headers.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 read_text_file_to_buffer.c \
-recursive_action.c safe_read.c safe_strncpy.c seek_ared_file.c syscalls.c \
-syslog_msg_with_name.c time_string.c trim.c untar.c unzip.c vdprintf.c \
-verror_msg.c vperror_msg.c wfopen.c xfuncs.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
+extract_archive.c fgets_str.c find_mount_point.c find_pid_by_name.c \
+find_root_device.c full_read.c full_write.c get_ar_headers.c get_console.c \
+get_last_path_component.c get_line_from_file.c get_tar_headers.c \
+get_tar_gz_headers.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 vdprintf.c verror_msg.c vperror_msg.c wfopen.c xfuncs.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
 LIBBB_OBJS=$(patsubst %.c,$(LIBBB)/%.o, $(LIBBB_CSRC))
 LIBBB_CFLAGS = -I$(LIBBB)
 ifneq ($(strip $(BB_SRC_DIR)),)
diff --git a/ar.c b/ar.c
index 08cd5c501..7359f4910 100644
--- a/ar.c
+++ b/ar.c
@@ -30,36 +30,27 @@
 
 extern int ar_main(int argc, char **argv)
 {
-	const int preserve_date = 1;	/* preserve original dates */
-	const int verbose = 2;		/* be verbose */
-	const int display = 4;		/* display contents */
-	const int extract_to_file = 8;	/* extract contents of archive */
-	const int extract_to_stdout = 16;	/* extract to stdout */
-
-	FILE *src_file = NULL, *dst_file = NULL;
-	int funct = 0, opt=0;
-
-	ar_headers_t *head, *ar_extract_list=NULL;
-
-	ar_extract_list = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
-	head = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
+	FILE *src_stream = NULL;
+	int extract_function = 0, opt = 0;
+	file_headers_t *head;
+	file_headers_t *ar_extract_list = NULL;
 
 	while ((opt = getopt(argc, argv, "ovtpx")) != -1) {
 		switch (opt) {
 		case 'o':
-			funct |= preserve_date;
+			extract_function |= extract_preserve_date;
 			break;
 		case 'v':
-			funct |= verbose;
+			extract_function |= extract_verbose_list;
 			break;
 		case 't':
-			funct |= display;
+			extract_function |= extract_list;
 			break;
 		case 'p':
-			funct |= extract_to_stdout;
+			extract_function |= extract_to_stdout;
 			break;
 		case 'x':
-			funct |= extract_to_file;
+			extract_function |= extract_all_to_fs;
 			break;
 		default:
 			show_usage();
@@ -71,57 +62,28 @@ extern int ar_main(int argc, char **argv)
 		show_usage();
 	}
 
-	if ( (src_file = wfopen(argv[optind], "r")) < 0) {
-		error_msg_and_die("Cannot read %s", argv[optind]);
-	}
+	src_stream = xfopen(argv[optind++], "r");
+	head = get_ar_headers(src_stream);
 
-	optind++;	
-	head = get_ar_headers(src_file);
 	/* find files to extract or display */
 	/* search through argv and build extract list */
-	for (;optind < argc; optind++) {
-		ar_headers_t *ar_entry;
-		ar_entry = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
-		ar_entry = head;
-		while (ar_entry->next != NULL) {
-			if (strcmp(argv[optind], ar_entry->name) == 0) {
-				ar_headers_t *tmp;
-				tmp = (ar_headers_t *) xmalloc(sizeof(ar_headers_t));
-				*tmp = *ar_extract_list;
-				*ar_extract_list = *ar_entry;
-				ar_extract_list->next = tmp;
-				break;					
-			}
-			ar_entry=ar_entry->next;
+	ar_extract_list = (file_headers_t *) xcalloc(1, sizeof(file_headers_t));
+	if (optind < argc) {
+		while (optind < argc) {
+			ar_extract_list = add_from_archive_list(head, ar_extract_list, argv[optind]);
+			optind++;
 		}
-	}
-
-	/* if individual files not found extract all files */	
-	if (ar_extract_list->next==NULL) {
+	} else {
 		ar_extract_list = head;
 	}
 
-	/* find files to extract or display */	
-	while (ar_extract_list->next != NULL) {
-		if (funct & extract_to_file) {
-			dst_file = wfopen(ar_extract_list->name, "w");				
-		}
-		else if (funct & extract_to_stdout) {
-			dst_file = stdout;
-		}
-		if ((funct & extract_to_file) || (funct & extract_to_stdout)) {
-			fseek(src_file, ar_extract_list->offset, SEEK_SET);
-			copy_file_chunk(src_file, dst_file, ar_extract_list->size);			
-		}
-		if (funct & verbose) {
-			printf("%s %d/%d %8d %s ", mode_string(ar_extract_list->mode), 
-				ar_extract_list->uid, ar_extract_list->gid,
-				(int) ar_extract_list->size, time_string(ar_extract_list->mtime));
-		}
-		if ((funct & display) || (funct & verbose)){
-			puts(ar_extract_list->name);
-		}
-		ar_extract_list = ar_extract_list->next;
-	}
+	/* If there isnt even one possible entry then abort */
+	if (ar_extract_list->name == NULL) {
+		error_msg_and_die("No files to extract");
+	}	
+
+	fseek(src_stream, 0, SEEK_SET);
+	extract_archive(src_stream, stdout, ar_extract_list, extract_function, "./");
+
 	return EXIT_SUCCESS;
 }
diff --git a/archival/ar.c b/archival/ar.c
index 08cd5c501..7359f4910 100644
--- a/archival/ar.c
+++ b/archival/ar.c
@@ -30,36 +30,27 @@
 
 extern int ar_main(int argc, char **argv)
 {
-	const int preserve_date = 1;	/* preserve original dates */
-	const int verbose = 2;		/* be verbose */
-	const int display = 4;		/* display contents */
-	const int extract_to_file = 8;	/* extract contents of archive */
-	const int extract_to_stdout = 16;	/* extract to stdout */
-
-	FILE *src_file = NULL, *dst_file = NULL;
-	int funct = 0, opt=0;
-
-	ar_headers_t *head, *ar_extract_list=NULL;
-
-	ar_extract_list = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
-	head = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
+	FILE *src_stream = NULL;
+	int extract_function = 0, opt = 0;
+	file_headers_t *head;
+	file_headers_t *ar_extract_list = NULL;
 
 	while ((opt = getopt(argc, argv, "ovtpx")) != -1) {
 		switch (opt) {
 		case 'o':
-			funct |= preserve_date;
+			extract_function |= extract_preserve_date;
 			break;
 		case 'v':
-			funct |= verbose;
+			extract_function |= extract_verbose_list;
 			break;
 		case 't':
-			funct |= display;
+			extract_function |= extract_list;
 			break;
 		case 'p':
-			funct |= extract_to_stdout;
+			extract_function |= extract_to_stdout;
 			break;
 		case 'x':
-			funct |= extract_to_file;
+			extract_function |= extract_all_to_fs;
 			break;
 		default:
 			show_usage();
@@ -71,57 +62,28 @@ extern int ar_main(int argc, char **argv)
 		show_usage();
 	}
 
-	if ( (src_file = wfopen(argv[optind], "r")) < 0) {
-		error_msg_and_die("Cannot read %s", argv[optind]);
-	}
+	src_stream = xfopen(argv[optind++], "r");
+	head = get_ar_headers(src_stream);
 
-	optind++;	
-	head = get_ar_headers(src_file);
 	/* find files to extract or display */
 	/* search through argv and build extract list */
-	for (;optind < argc; optind++) {
-		ar_headers_t *ar_entry;
-		ar_entry = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
-		ar_entry = head;
-		while (ar_entry->next != NULL) {
-			if (strcmp(argv[optind], ar_entry->name) == 0) {
-				ar_headers_t *tmp;
-				tmp = (ar_headers_t *) xmalloc(sizeof(ar_headers_t));
-				*tmp = *ar_extract_list;
-				*ar_extract_list = *ar_entry;
-				ar_extract_list->next = tmp;
-				break;					
-			}
-			ar_entry=ar_entry->next;
+	ar_extract_list = (file_headers_t *) xcalloc(1, sizeof(file_headers_t));
+	if (optind < argc) {
+		while (optind < argc) {
+			ar_extract_list = add_from_archive_list(head, ar_extract_list, argv[optind]);
+			optind++;
 		}
-	}
-
-	/* if individual files not found extract all files */	
-	if (ar_extract_list->next==NULL) {
+	} else {
 		ar_extract_list = head;
 	}
 
-	/* find files to extract or display */	
-	while (ar_extract_list->next != NULL) {
-		if (funct & extract_to_file) {
-			dst_file = wfopen(ar_extract_list->name, "w");				
-		}
-		else if (funct & extract_to_stdout) {
-			dst_file = stdout;
-		}
-		if ((funct & extract_to_file) || (funct & extract_to_stdout)) {
-			fseek(src_file, ar_extract_list->offset, SEEK_SET);
-			copy_file_chunk(src_file, dst_file, ar_extract_list->size);			
-		}
-		if (funct & verbose) {
-			printf("%s %d/%d %8d %s ", mode_string(ar_extract_list->mode), 
-				ar_extract_list->uid, ar_extract_list->gid,
-				(int) ar_extract_list->size, time_string(ar_extract_list->mtime));
-		}
-		if ((funct & display) || (funct & verbose)){
-			puts(ar_extract_list->name);
-		}
-		ar_extract_list = ar_extract_list->next;
-	}
+	/* If there isnt even one possible entry then abort */
+	if (ar_extract_list->name == NULL) {
+		error_msg_and_die("No files to extract");
+	}	
+
+	fseek(src_stream, 0, SEEK_SET);
+	extract_archive(src_stream, stdout, ar_extract_list, extract_function, "./");
+
 	return EXIT_SUCCESS;
 }
diff --git a/archival/dpkg.c b/archival/dpkg.c
index 0010df537..4224672ec 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -13,7 +13,6 @@
 
 #include "busybox.h"
 
-
 #define DEPENDSMAX	64	/* maximum number of depends we can handle */
 
 /* Should we do full dependency checking? */
@@ -487,7 +486,7 @@ static void *status_read(void)
 		return(NULL);
 	}
 
-	while ( (package_control_buffer = read_text_file_to_buffer(f)) != NULL) {
+	while ( (package_control_buffer = fgets_str(f, "\n\n")) != NULL) {
 		m = (package_t *)xcalloc(1, sizeof(package_t));
 		fill_package_struct(m, package_control_buffer);
 		if (m->package) {
@@ -650,24 +649,26 @@ static int dpkg_doconfigure(package_t *pkg)
 
 static int dpkg_dounpack(package_t *pkg)
 {
-	int r = 0;
+	FILE *out_stream;
+	char *info_prefix;
 	int status = TRUE;
-	char *lst_path;
+	int r = 0;
 
 	DPRINTF("Unpacking %s\n", pkg->package);
 
 	/* extract the data file */
-	deb_extract(pkg->filename, extract_extract, "/", NULL);
+	deb_extract(pkg->filename, stdout, (extract_data_tar_gz | extract_all_to_fs), "/", NULL);
 
 	/* extract the control files */
-	deb_extract(pkg->filename, extract_control, infodir, pkg->package);
+	info_prefix = (char *) malloc(strlen(pkg->package) + strlen(infodir) + 2 + 5 + 1);
+	sprintf(info_prefix, "%s/%s.", infodir, pkg->package);
+	deb_extract(pkg->package, stdout, (extract_control_tar_gz | extract_all_to_fs), info_prefix, NULL);
 
 	/* Create the list file */
-	lst_path = xmalloc(strlen(infodir) + strlen(pkg->package) + 6);
-	strcpy(lst_path, infodir);
-	strcat(lst_path, pkg->package);
-	strcat(lst_path, ".list");
-	deb_extract(pkg->filename, extract_contents_to_file, lst_path, NULL);
+	strcat(info_prefix, "list");
+	out_stream = wfopen(info_prefix, "w");			
+	deb_extract(pkg->package, out_stream, (extract_data_tar_gz | extract_list), NULL, NULL);
+	fclose(out_stream);
 
 	pkg->state_want = state_want_install;
 	pkg->state_flag = state_flag_ok;
@@ -692,7 +693,7 @@ static int dpkg_read_control(package_t *pkg)
 	if ((pkg_file = wfopen(pkg->filename, "r")) == NULL) {
 		return EXIT_FAILURE;
 	}
-	control_buffer = deb_extract(pkg->filename, extract_field, NULL, NULL);
+	control_buffer = deb_extract(pkg->filename, stdout, (extract_control_tar_gz | extract_one_to_buffer), NULL, "./control");
 	fill_package_struct(pkg, control_buffer);
 	return EXIT_SUCCESS;
 }
diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c
index 17b5476d0..400f2385a 100644
--- a/archival/dpkg_deb.c
+++ b/archival/dpkg_deb.c
@@ -21,33 +21,54 @@
 
 extern int dpkg_deb_main(int argc, char **argv)
 {
-	char *argument = NULL;
+	char *prefix = NULL;
+	char *filename = NULL;
 	char *output_buffer = NULL;
 	int opt = 0;
-	int optflag = 0;	
+	int arg_type = 0;
+	int deb_extract_funct = 0;	
 	
+	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;
+
 	while ((opt = getopt(argc, argv, "ceftXxI")) != -1) {
 		switch (opt) {
 			case 'c':
-				optflag |= extract_contents;
+				deb_extract_funct |= extract_data_tar_gz;
+				deb_extract_funct |= extract_verbose_list;
 				break;
 			case 'e':
-				optflag |= extract_control;
+				arg_type = arg_type_prefix;
+				deb_extract_funct |= extract_control_tar_gz;
+				deb_extract_funct |= extract_all_to_fs;
 				break;
 			case 'f':
-				optflag |= extract_field;
+				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':
-				optflag |= extract_fsys_tarfile;
+			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;
 				break;
 			case 'X':
-				optflag |= extract_verbose_extract;
-				break;
+				arg_type = arg_type_prefix;
+				deb_extract_funct |= extract_data_tar_gz;
+				deb_extract_funct |= extract_all_to_fs;
+				deb_extract_funct |= extract_list;
 			case 'x':
-				optflag |= extract_extract;
+				arg_type = arg_type_prefix;
+				deb_extract_funct |= extract_data_tar_gz;
+				deb_extract_funct |= extract_all_to_fs;
 				break;
 			case 'I':
-				optflag |= extract_info;
+				arg_type = arg_type_filename;
+				deb_extract_funct |= extract_control_tar_gz;
+				deb_extract_funct |= extract_one_to_buffer;
 				break;
 			default:
 				show_usage();
@@ -58,48 +79,42 @@ extern int dpkg_deb_main(int argc, char **argv)
 		show_usage();
 	}
 
-	switch (optflag) {
-		case (extract_control):
-		case (extract_extract):
-		case (extract_verbose_extract):
-			/* argument is a dir name */
-			if ( (optind + 1) == argc ) {
-				argument = xstrdup("DEBIAN");
-			} else {
-				argument = xstrdup(argv[optind + 1]);
+	/* 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, "/");
 			}
-			break;
-		case (extract_field):
-			/* argument is a control field name */
-			if ((optind + 1) != argc) {
-				argument = xstrdup(argv[optind + 1]);				
-			}
-			break;
-		case (extract_info):
-			/* argument is a control field name */
-			if ((optind + 1) != argc) {
-				argument = xstrdup(argv[optind + 1]);
-				break;
-			} else {
-				error_msg("-I currently requires a filename to be specifies");
-				return(EXIT_FAILURE);
-			}
-			/* argument is a filename */
-		default:
+		}
 	}
 
-	output_buffer = deb_extract(argv[optind], optflag, argument, NULL);
+	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");
+		}
+	}
 
-	if (optflag & extract_field) {
+	output_buffer = deb_extract(argv[optind], stdout, deb_extract_funct, prefix, filename);
+
+	if ((arg_type == arg_type_filename) && (output_buffer != NULL)) {
+		puts(output_buffer);
+	}
+	else if (arg_type == arg_type_field) {
 		char *field = NULL;
-		int field_length = 0;
 		int field_start = 0;
 
 		while ((field = read_package_field(&output_buffer[field_start])) != NULL) {
-			field_length = strlen(field);
-			field_start += (field_length + 1);
-			if (strstr(field, argument) == field) {
-				puts(field + strlen(argument) + 2);
+			field_start += (strlen(field) + 1);
+			if (strstr(field, argv[optind + 1]) == field) {
+				puts(field + strlen(argv[optind + 1]) + 2);
 			}
 			free(field);
 		}
diff --git a/dpkg.c b/dpkg.c
index 0010df537..4224672ec 100644
--- a/dpkg.c
+++ b/dpkg.c
@@ -13,7 +13,6 @@
 
 #include "busybox.h"
 
-
 #define DEPENDSMAX	64	/* maximum number of depends we can handle */
 
 /* Should we do full dependency checking? */
@@ -487,7 +486,7 @@ static void *status_read(void)
 		return(NULL);
 	}
 
-	while ( (package_control_buffer = read_text_file_to_buffer(f)) != NULL) {
+	while ( (package_control_buffer = fgets_str(f, "\n\n")) != NULL) {
 		m = (package_t *)xcalloc(1, sizeof(package_t));
 		fill_package_struct(m, package_control_buffer);
 		if (m->package) {
@@ -650,24 +649,26 @@ static int dpkg_doconfigure(package_t *pkg)
 
 static int dpkg_dounpack(package_t *pkg)
 {
-	int r = 0;
+	FILE *out_stream;
+	char *info_prefix;
 	int status = TRUE;
-	char *lst_path;
+	int r = 0;
 
 	DPRINTF("Unpacking %s\n", pkg->package);
 
 	/* extract the data file */
-	deb_extract(pkg->filename, extract_extract, "/", NULL);
+	deb_extract(pkg->filename, stdout, (extract_data_tar_gz | extract_all_to_fs), "/", NULL);
 
 	/* extract the control files */
-	deb_extract(pkg->filename, extract_control, infodir, pkg->package);
+	info_prefix = (char *) malloc(strlen(pkg->package) + strlen(infodir) + 2 + 5 + 1);
+	sprintf(info_prefix, "%s/%s.", infodir, pkg->package);
+	deb_extract(pkg->package, stdout, (extract_control_tar_gz | extract_all_to_fs), info_prefix, NULL);
 
 	/* Create the list file */
-	lst_path = xmalloc(strlen(infodir) + strlen(pkg->package) + 6);
-	strcpy(lst_path, infodir);
-	strcat(lst_path, pkg->package);
-	strcat(lst_path, ".list");
-	deb_extract(pkg->filename, extract_contents_to_file, lst_path, NULL);
+	strcat(info_prefix, "list");
+	out_stream = wfopen(info_prefix, "w");			
+	deb_extract(pkg->package, out_stream, (extract_data_tar_gz | extract_list), NULL, NULL);
+	fclose(out_stream);
 
 	pkg->state_want = state_want_install;
 	pkg->state_flag = state_flag_ok;
@@ -692,7 +693,7 @@ static int dpkg_read_control(package_t *pkg)
 	if ((pkg_file = wfopen(pkg->filename, "r")) == NULL) {
 		return EXIT_FAILURE;
 	}
-	control_buffer = deb_extract(pkg->filename, extract_field, NULL, NULL);
+	control_buffer = deb_extract(pkg->filename, stdout, (extract_control_tar_gz | extract_one_to_buffer), NULL, "./control");
 	fill_package_struct(pkg, control_buffer);
 	return EXIT_SUCCESS;
 }
diff --git a/dpkg_deb.c b/dpkg_deb.c
index 17b5476d0..400f2385a 100644
--- a/dpkg_deb.c
+++ b/dpkg_deb.c
@@ -21,33 +21,54 @@
 
 extern int dpkg_deb_main(int argc, char **argv)
 {
-	char *argument = NULL;
+	char *prefix = NULL;
+	char *filename = NULL;
 	char *output_buffer = NULL;
 	int opt = 0;
-	int optflag = 0;	
+	int arg_type = 0;
+	int deb_extract_funct = 0;	
 	
+	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;
+
 	while ((opt = getopt(argc, argv, "ceftXxI")) != -1) {
 		switch (opt) {
 			case 'c':
-				optflag |= extract_contents;
+				deb_extract_funct |= extract_data_tar_gz;
+				deb_extract_funct |= extract_verbose_list;
 				break;
 			case 'e':
-				optflag |= extract_control;
+				arg_type = arg_type_prefix;
+				deb_extract_funct |= extract_control_tar_gz;
+				deb_extract_funct |= extract_all_to_fs;
 				break;
 			case 'f':
-				optflag |= extract_field;
+				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':
-				optflag |= extract_fsys_tarfile;
+			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;
 				break;
 			case 'X':
-				optflag |= extract_verbose_extract;
-				break;
+				arg_type = arg_type_prefix;
+				deb_extract_funct |= extract_data_tar_gz;
+				deb_extract_funct |= extract_all_to_fs;
+				deb_extract_funct |= extract_list;
 			case 'x':
-				optflag |= extract_extract;
+				arg_type = arg_type_prefix;
+				deb_extract_funct |= extract_data_tar_gz;
+				deb_extract_funct |= extract_all_to_fs;
 				break;
 			case 'I':
-				optflag |= extract_info;
+				arg_type = arg_type_filename;
+				deb_extract_funct |= extract_control_tar_gz;
+				deb_extract_funct |= extract_one_to_buffer;
 				break;
 			default:
 				show_usage();
@@ -58,48 +79,42 @@ extern int dpkg_deb_main(int argc, char **argv)
 		show_usage();
 	}
 
-	switch (optflag) {
-		case (extract_control):
-		case (extract_extract):
-		case (extract_verbose_extract):
-			/* argument is a dir name */
-			if ( (optind + 1) == argc ) {
-				argument = xstrdup("DEBIAN");
-			} else {
-				argument = xstrdup(argv[optind + 1]);
+	/* 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, "/");
 			}
-			break;
-		case (extract_field):
-			/* argument is a control field name */
-			if ((optind + 1) != argc) {
-				argument = xstrdup(argv[optind + 1]);				
-			}
-			break;
-		case (extract_info):
-			/* argument is a control field name */
-			if ((optind + 1) != argc) {
-				argument = xstrdup(argv[optind + 1]);
-				break;
-			} else {
-				error_msg("-I currently requires a filename to be specifies");
-				return(EXIT_FAILURE);
-			}
-			/* argument is a filename */
-		default:
+		}
 	}
 
-	output_buffer = deb_extract(argv[optind], optflag, argument, NULL);
+	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");
+		}
+	}
 
-	if (optflag & extract_field) {
+	output_buffer = deb_extract(argv[optind], stdout, deb_extract_funct, prefix, filename);
+
+	if ((arg_type == arg_type_filename) && (output_buffer != NULL)) {
+		puts(output_buffer);
+	}
+	else if (arg_type == arg_type_field) {
 		char *field = NULL;
-		int field_length = 0;
 		int field_start = 0;
 
 		while ((field = read_package_field(&output_buffer[field_start])) != NULL) {
-			field_length = strlen(field);
-			field_start += (field_length + 1);
-			if (strstr(field, argument) == field) {
-				puts(field + strlen(argument) + 2);
+			field_start += (strlen(field) + 1);
+			if (strstr(field, argv[optind + 1]) == field) {
+				puts(field + strlen(argv[optind + 1]) + 2);
 			}
 			free(field);
 		}
diff --git a/include/libbb.h b/include/libbb.h
index f99d25780..f24a38109 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -213,36 +213,41 @@ char *xreadlink(const char *path);
 char *concat_path_file(const char *path, const char *filename);
 char *last_char_is(const char *s, int c);
 
-typedef struct ar_headers_s {
+typedef struct file_headers_s {
 	char *name;
+	char *link_name;
 	off_t size;
 	uid_t uid;
 	gid_t gid;
 	mode_t mode;
 	time_t mtime;
 	off_t offset;
-	struct ar_headers_s *next;
-} ar_headers_t;
-extern ar_headers_t *get_ar_headers(FILE *in_file);
-extern int seek_ared_file(FILE *in_file, ar_headers_t *headers, const char *tar_gz_file);
+	dev_t device;
+	struct file_headers_s *next;
+} file_headers_t;
+file_headers_t *get_ar_headers(FILE *in_file);
+file_headers_t *get_tar_headers(FILE *tar_stream);
+file_headers_t *get_tar_gz_headers(FILE *compressed_stream);
+file_headers_t *append_archive_list(file_headers_t *head, file_headers_t *tail_entry);
+file_headers_t *add_from_archive_list(file_headers_t *master_list, file_headers_t *new_list, const char *name);
 
-typedef enum extract_function_e {
-	extract_contents = 1,
-	extract_control = 2,
-	extract_info = 4,
-	extract_extract = 8,
-	extract_verbose_extract = 16,
-	extract_list = 32,
-	extract_fsys_tarfile = 64,
-	extract_field = 128,
-	extract_contents_to_file = 256
-} extract_function_t;
-extern char *deb_extract(const char *package_filename, int function,
-	const char *argument, const char *argument2);
-extern char *untar(FILE *src_tar_file, FILE *output, int untar_function,
-	const char *argument, const char *file_prefix);
-extern char *read_text_file_to_buffer(FILE *src_file);
-extern char *read_package_field(const char *package_buffer);
+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
+};
+char *extract_archive(FILE *src_stream, FILE *out_stream, file_headers_t *extract_headers, int function, const char *prefix);
+char *deb_extract(const char *package_filename, FILE *out_stream, const int function,
+	const char *prefix, const char *filename);
+char *read_package_field(const char *package_buffer);
+int seek_sub_file(FILE *in_file, file_headers_t *headers, const char *tar_gz_file);
+char *fgets_str(FILE *file, const char *terminating_string);
 
 extern int unzip(FILE *l_in_file, FILE *l_out_file);
 extern void gz_close(int gunzip_pid);
diff --git a/libbb/deb_extract.c b/libbb/deb_extract.c
index 36cebfa51..f64193970 100644
--- a/libbb/deb_extract.c
+++ b/libbb/deb_extract.c
@@ -27,71 +27,97 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
-#include <signal.h>
 #include "libbb.h"
 
+int seek_sub_file(FILE *in_file, file_headers_t *headers, const char *tar_gz_file)
+{
+	/* find the headers for the specified .tar.gz file */
+	while (headers != NULL) {
+		if (strcmp(headers->name, tar_gz_file) == 0) {
+			fseek(in_file, headers->offset, SEEK_SET);
+			return(EXIT_SUCCESS);
+		}
+		headers = headers->next;
+	}
+
+	return(EXIT_FAILURE);
+}
+
 /*
  * The contents of argument depend on the value of function.
  * It is either a dir name or a control file or field name(see dpkg_deb.c)
  */
-extern char *deb_extract(const char *package_filename, const int function, const char *argument, const char *argument2)
+char *deb_extract(const char *package_filename, FILE *out_stream, const int extract_function,
+	const char *prefix, const char *filename)
 {
+	FILE *deb_stream, *uncompressed_stream;
+	file_headers_t *ar_headers = NULL;
+	file_headers_t *tar_headers = NULL;
+	file_headers_t *list_ptr;
+	file_headers_t *deb_extract_list = (file_headers_t *) calloc(1, sizeof(file_headers_t));
 
-	FILE *deb_file, *uncompressed_file;
-	ar_headers_t *headers = NULL;
 	char *ared_file = NULL;
 	char *output_buffer = NULL;
 	int gunzip_pid;
 
-	switch (function) {
-		case (extract_info):
-		case (extract_control):
-		case (extract_field):
-			ared_file = xstrdup("control.tar.gz");
-			break;
-		default:
-			ared_file = xstrdup("data.tar.gz");
-			break;
+	if (extract_function & extract_control_tar_gz) {
+		ared_file = xstrdup("control.tar.gz");
+	}
+	else if (extract_function & extract_data_tar_gz) {		
+		ared_file = xstrdup("data.tar.gz");
 	}
 
 	/* open the debian package to be worked on */
-	deb_file = wfopen(package_filename, "r");
+	deb_stream = wfopen(package_filename, "r");
 
-	headers = (ar_headers_t *) xmalloc(sizeof(ar_headers_t));	
+	ar_headers = (file_headers_t *) xmalloc(sizeof(file_headers_t));	
 	
 	/* get a linked list of all ar entries */
-	if ((headers = get_ar_headers(deb_file)) == NULL) {
+	ar_headers = get_ar_headers(deb_stream);
+	if (ar_headers == NULL) {
 		error_msg("Couldnt get ar headers\n");
 		return(NULL);
 	}
 
 	/* seek to the start of the .tar.gz file within the ar file*/
-	if (seek_ared_file(deb_file, headers, ared_file) == EXIT_FAILURE) {
+	fseek(deb_stream, 0, SEEK_SET);
+	if (seek_sub_file(deb_stream, ar_headers, ared_file) == EXIT_FAILURE) {
+		error_msg("Couldnt seek to file %s", ared_file);
+	}
+
+	/* get a linked list of all tar entries */
+	tar_headers = get_tar_gz_headers(deb_stream);
+	if (tar_headers == NULL) {
+		error_msg("Couldnt get tar headers\n");
+		return(NULL);
+	}
+
+	if (extract_function & extract_one_to_buffer) {
+		list_ptr = tar_headers;
+		while (list_ptr != NULL) {
+			if (strcmp(filename, list_ptr->name) == 0) {
+				deb_extract_list = append_archive_list(deb_extract_list, list_ptr);
+				break;
+			}
+			list_ptr = list_ptr->next;
+		}
+	} else {
+		deb_extract_list = tar_headers;
+	}
+
+	/* seek to the start of the .tar.gz file within the ar file*/
+	if (seek_sub_file(deb_stream, ar_headers, ared_file) == EXIT_FAILURE) {
 		error_msg("Couldnt seek to ar file");
 	}
 
 	/* open a stream of decompressed data */
-	uncompressed_file = fdopen(gz_open(deb_file, &gunzip_pid), "r");
+	uncompressed_stream = fdopen(gz_open(deb_stream, &gunzip_pid), "r");
 
-	if (function & extract_fsys_tarfile) {
-		copy_file_chunk(uncompressed_file, stdout, -1);
-	} else {
-		FILE *output;
+	output_buffer = extract_archive(uncompressed_stream, out_stream, deb_extract_list, extract_function, prefix);
 
-		if (function & extract_contents_to_file) {
-			output = wfopen(argument, "w");
-		} else {
-			output = stdout;
-		}
-
-		output_buffer = untar(uncompressed_file, output, function, argument, argument2);
-		if (output != stdout) {
-			fclose(output);
-		}
-	}
 	gz_close(gunzip_pid);
-	fclose(deb_file);
-	fclose(uncompressed_file);
+	fclose(deb_stream);
+	fclose(uncompressed_stream);
 	free(ared_file);
 
 	return(output_buffer);
diff --git a/libbb/get_ar_headers.c b/libbb/get_ar_headers.c
index 151caafe1..414498b4f 100644
--- a/libbb/get_ar_headers.c
+++ b/libbb/get_ar_headers.c
@@ -30,7 +30,7 @@
 #include <unistd.h>
 #include "libbb.h"
 
-extern ar_headers_t *get_ar_headers(FILE *in_file)
+file_headers_t *get_ar_headers(FILE *in_file)
 {
 	typedef struct raw_ar_header_s {	/* Byte Offset */
 		char name[16];	/*  0-15 */
@@ -44,7 +44,7 @@ extern ar_headers_t *get_ar_headers(FILE *in_file)
 
 	raw_ar_header_t raw_ar_header;
 
-	ar_headers_t *ar_list, *ar_tmp, *ar_entry;
+	file_headers_t *ar_list, *ar_entry;
 	char ar_magic[8];
 	char *long_name=NULL;
 	
@@ -59,10 +59,10 @@ extern ar_headers_t *get_ar_headers(FILE *in_file)
 		return(NULL);
 	}
 	
-	ar_list = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
+	ar_list = (file_headers_t *) xcalloc(1, sizeof(file_headers_t));
 
 	while (fread((char *) &raw_ar_header, 1, 60, in_file) == 60) {
-		ar_entry = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
+		ar_entry = (file_headers_t *) xcalloc(1, sizeof(file_headers_t));
 		/* check the end of header markers are valid */
 		if ((raw_ar_header.fmag[0] != '`') || (raw_ar_header.fmag[1] != '\n')) {
 			char newline;
@@ -113,12 +113,7 @@ extern ar_headers_t *get_ar_headers(FILE *in_file)
 
 		fseek(in_file, (off_t) ar_entry->size, SEEK_CUR);
 
-		ar_tmp = (ar_headers_t *) xcalloc(1, sizeof(ar_headers_t));
-		*ar_tmp = *ar_list;
-		*ar_list = *ar_entry;
-		free(ar_entry);
-		ar_list->next = ar_tmp;
+		ar_list = append_archive_list(ar_list, ar_entry);
 	}
-
 	return(ar_list);
 }
diff --git a/libbb/libbb.h b/libbb/libbb.h
index f99d25780..f24a38109 100644
--- a/libbb/libbb.h
+++ b/libbb/libbb.h
@@ -213,36 +213,41 @@ char *xreadlink(const char *path);
 char *concat_path_file(const char *path, const char *filename);
 char *last_char_is(const char *s, int c);
 
-typedef struct ar_headers_s {
+typedef struct file_headers_s {
 	char *name;
+	char *link_name;
 	off_t size;
 	uid_t uid;
 	gid_t gid;
 	mode_t mode;
 	time_t mtime;
 	off_t offset;
-	struct ar_headers_s *next;
-} ar_headers_t;
-extern ar_headers_t *get_ar_headers(FILE *in_file);
-extern int seek_ared_file(FILE *in_file, ar_headers_t *headers, const char *tar_gz_file);
+	dev_t device;
+	struct file_headers_s *next;
+} file_headers_t;
+file_headers_t *get_ar_headers(FILE *in_file);
+file_headers_t *get_tar_headers(FILE *tar_stream);
+file_headers_t *get_tar_gz_headers(FILE *compressed_stream);
+file_headers_t *append_archive_list(file_headers_t *head, file_headers_t *tail_entry);
+file_headers_t *add_from_archive_list(file_headers_t *master_list, file_headers_t *new_list, const char *name);
 
-typedef enum extract_function_e {
-	extract_contents = 1,
-	extract_control = 2,
-	extract_info = 4,
-	extract_extract = 8,
-	extract_verbose_extract = 16,
-	extract_list = 32,
-	extract_fsys_tarfile = 64,
-	extract_field = 128,
-	extract_contents_to_file = 256
-} extract_function_t;
-extern char *deb_extract(const char *package_filename, int function,
-	const char *argument, const char *argument2);
-extern char *untar(FILE *src_tar_file, FILE *output, int untar_function,
-	const char *argument, const char *file_prefix);
-extern char *read_text_file_to_buffer(FILE *src_file);
-extern char *read_package_field(const char *package_buffer);
+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
+};
+char *extract_archive(FILE *src_stream, FILE *out_stream, file_headers_t *extract_headers, int function, const char *prefix);
+char *deb_extract(const char *package_filename, FILE *out_stream, const int function,
+	const char *prefix, const char *filename);
+char *read_package_field(const char *package_buffer);
+int seek_sub_file(FILE *in_file, file_headers_t *headers, const char *tar_gz_file);
+char *fgets_str(FILE *file, const char *terminating_string);
 
 extern int unzip(FILE *l_in_file, FILE *l_out_file);
 extern void gz_close(int gunzip_pid);