--- a/libopkg/parse_util.c
+++ b/libopkg/parse_util.c
@@ -22,6 +22,7 @@
 #include "libbb/libbb.h"
 
 #include "parse_util.h"
+#include "pkg_parse.h"
 
 int
 is_field(const char *type, const char *line)
@@ -86,3 +87,84 @@ parse_list(const char *raw, unsigned int
 	*count = line_count;
 	return depends;
 }
+
+int
+parse_from_stream_nomalloc(parse_line_t parse_line, void *item, FILE *fp, uint mask,
+						char **buf0, size_t buf0len)
+{
+	int ret, lineno;
+	char *buf, *nl;
+	size_t buflen;
+
+	lineno = 1;
+	ret = 0;
+
+	buflen = buf0len;
+	buf = *buf0;
+	buf[0] = '\0';
+
+	while (1) {
+		if (fgets(buf, (int)buflen, fp) == NULL) {
+			if (ferror(fp)) {
+				opkg_perror(ERROR, "fgets");
+				ret = -1;
+			} else if (strlen(*buf0) == buf0len-1) {
+				opkg_msg(ERROR, "Missing new line character"
+						" at end of file!\n");
+				parse_line(item, *buf0, mask);
+			}
+			break;
+		}
+
+		nl = strchr(buf, '\n');
+		if (nl == NULL) {
+			if (strlen(buf) < buflen-1) {
+				/*
+				 * Line could be exactly buflen-1 long and
+				 * missing a newline, but we won't know until
+				 * fgets fails to read more data.
+				 */
+				opkg_msg(ERROR, "Missing new line character"
+						" at end of file!\n");
+				parse_line(item, *buf0, mask);
+				break;
+			}
+			if (buf0len >= EXCESSIVE_LINE_LEN) {
+				opkg_msg(ERROR, "Excessively long line at "
+					"%d. Corrupt file?\n",
+					lineno);
+				ret = -1;
+				break;
+			}
+
+			/*
+			 * Realloc and point buf past the data already read,
+			 * at the NULL terminator inserted by fgets.
+			 * |<--------------- buf0len ----------------->|
+			 * |                     |<------- buflen ---->|
+			 * |---------------------|---------------------|
+			 * buf0                   buf
+			 */
+			buflen = buf0len +1;
+			buf0len *= 2;
+			*buf0 = xrealloc(*buf0, buf0len);
+			buf = *buf0 + buflen -2;
+
+			continue;
+		}
+
+		*nl = '\0';
+
+		lineno++;
+
+		if (parse_line(item, *buf0, mask))
+			break;
+
+		buf = *buf0;
+		buflen = buf0len;
+		buf[0] = '\0';
+	}
+
+	return ret;
+}
+
--- a/libopkg/parse_util.h
+++ b/libopkg/parse_util.h
@@ -22,4 +22,8 @@ int is_field(const char *type, const cha
 char *parse_simple(const char *type, const char *line);
 char **parse_list(const char *raw, unsigned int *count, const char sep, int skip_field);
 
+typedef int (*parse_line_t)(void *, const char *, uint);
+int parse_from_stream_nomalloc(parse_line_t parse_line, void *item, FILE *fp, uint mask,
+						char **buf0, size_t buf0len);
+
 #endif
--- a/libopkg/pkg_hash.c
+++ b/libopkg/pkg_hash.c
@@ -23,6 +23,7 @@
 #include "opkg_message.h"
 #include "pkg_vec.h"
 #include "pkg_hash.h"
+#include "parse_util.h"
 #include "pkg_parse.h"
 #include "opkg_utils.h"
 #include "sprintf_alloc.h"
@@ -119,8 +120,14 @@ pkg_hash_add_from_file(const char *file_
 		pkg->src = src;
 		pkg->dest = dest;
 
-		ret = pkg_parse_from_stream_nomalloc(pkg, fp, 0,
+		ret = parse_from_stream_nomalloc(pkg_parse_line, pkg, fp, 0,
 				&buf, len);
+
+		if (pkg->name == NULL) {
+			/* probably just a blank line */
+			ret = 1;
+		}
+
 		if (ret) {
 			pkg_deinit (pkg);
 			free(pkg);
--- a/libopkg/pkg_parse.c
+++ b/libopkg/pkg_parse.c
@@ -104,9 +104,11 @@ get_arch_priority(const char *arch)
 	return 0;
 }
 
-static int
-pkg_parse_line(pkg_t *pkg, const char *line, uint mask)
+int
+pkg_parse_line(void *ptr, const char *line, uint mask)
 {
+	pkg_t *pkg = (pkg_t *) ptr;
+
 	/* these flags are a bit hackish... */
 	static int reading_conffiles = 0, reading_description = 0;
 	int ret = 0;
@@ -266,91 +268,6 @@ dont_reset_flags:
 }
 
 int
-pkg_parse_from_stream_nomalloc(pkg_t *pkg, FILE *fp, uint mask,
-						char **buf0, size_t buf0len)
-{
-	int ret, lineno;
-	char *buf, *nl;
-	size_t buflen;
-
-	lineno = 1;
-	ret = 0;
-
-	buflen = buf0len;
-	buf = *buf0;
-	buf[0] = '\0';
-
-	while (1) {
-		if (fgets(buf, (int)buflen, fp) == NULL) {
-			if (ferror(fp)) {
-				opkg_perror(ERROR, "fgets");
-				ret = -1;
-			} else if (strlen(*buf0) == buf0len-1) {
-				opkg_msg(ERROR, "Missing new line character"
-						" at end of file!\n");
-				pkg_parse_line(pkg, *buf0, mask);
-			}
-			break;
-		}
-
-		nl = strchr(buf, '\n');
-		if (nl == NULL) {
-			if (strlen(buf) < buflen-1) {
-				/*
-				 * Line could be exactly buflen-1 long and
-				 * missing a newline, but we won't know until
-				 * fgets fails to read more data.
-				 */
-				opkg_msg(ERROR, "Missing new line character"
-						" at end of file!\n");
-				pkg_parse_line(pkg, *buf0, mask);
-				break;
-			}
-			if (buf0len >= EXCESSIVE_LINE_LEN) {
-				opkg_msg(ERROR, "Excessively long line at "
-					"%d. Corrupt file?\n",
-					lineno);
-				ret = -1;
-				break;
-			}
-
-			/*
-			 * Realloc and point buf past the data already read,
-			 * at the NULL terminator inserted by fgets.
-			 * |<--------------- buf0len ----------------->|
-			 * |                     |<------- buflen ---->|
-			 * |---------------------|---------------------|
-			 * buf0                   buf
-			 */
-			buflen = buf0len +1;
-			buf0len *= 2;
-			*buf0 = xrealloc(*buf0, buf0len);
-			buf = *buf0 + buflen -2;
-
-			continue;
-		}
-
-		*nl = '\0';
-
-		lineno++;
-
-		if (pkg_parse_line(pkg, *buf0, mask))
-			break;
-
-		buf = *buf0;
-		buflen = buf0len;
-		buf[0] = '\0';
-	}
-
-	if (pkg->name == NULL) {
-		/* probably just a blank line */
-		ret = 1;
-	}
-
-	return ret;
-}
-
-int
 pkg_parse_from_stream(pkg_t *pkg, FILE *fp, uint mask)
 {
 	int ret;
@@ -358,8 +275,13 @@ pkg_parse_from_stream(pkg_t *pkg, FILE *
 	const size_t len = 4096;
 
 	buf = xmalloc(len);
-	ret = pkg_parse_from_stream_nomalloc(pkg, fp, mask, &buf, len);
+	ret = parse_from_stream_nomalloc(pkg_parse_line, pkg, fp, mask, &buf, len);
 	free(buf);
 
+	if (pkg->name == NULL) {
+		/* probably just a blank line */
+		ret = 1;
+	}
+
 	return ret;
 }
--- a/libopkg/pkg_parse.h
+++ b/libopkg/pkg_parse.h
@@ -18,10 +18,11 @@
 #ifndef PKG_PARSE_H
 #define PKG_PARSE_H
 
+#include "pkg.h"
+
 int parse_version(pkg_t *pkg, const char *raw);
 int pkg_parse_from_stream(pkg_t *pkg, FILE *fp, uint mask);
-int pkg_parse_from_stream_nomalloc(pkg_t *pkg, FILE *fp, uint mask,
-						char **buf0, size_t buf0len);
+int pkg_parse_line(void *ptr, const char *line, uint mask);
 
 #define EXCESSIVE_LINE_LEN	(4096 << 8)
 
--- a/libopkg/release_parse.c
+++ b/libopkg/release_parse.c
@@ -23,8 +23,10 @@
 #include "parse_util.h"
 
 static int
-release_parse_line(release_t *release, const char *line)
+release_parse_line(void *ptr, const char *line, uint mask)
 {
+	release_t *release = (release_t *) ptr;
+
 	int ret = 0;
 	unsigned int count = 0;
 	char **list = 0;
@@ -111,25 +113,14 @@ dont_reset_flags:
 int
 release_parse_from_stream(release_t *release, FILE *fp)
 {
-	int ret = 0;
-	char *buf = NULL;
-	size_t buflen, nread;
-
-	nread = getline(&buf, &buflen, fp);
-	while ( nread != -1 ) {
-		if (buf[nread-1] == '\n') buf[nread-1] = '\0';
-		if (release_parse_line(release, buf))
-                        opkg_msg(DEBUG, "Failed to parse release line for %s:\n\t%s\n",
-					release->name, buf);
-		nread = getline(&buf, &buflen, fp);
-	}
-
-	if (!feof(fp)) {
-		opkg_perror(ERROR, "Problems reading Release file for %sd\n", release->name);
-		ret = -1;
-	}
+	int ret;
+	char *buf;
+	const size_t len = 4096;
 
+	buf = xmalloc(len);
+	ret = parse_from_stream_nomalloc(release_parse_line, release, fp, 0, &buf, len);
 	free(buf);
+
 	return ret;
 }