summaryrefslogtreecommitdiff
path: root/bundle.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2011-10-13 15:12:02 -0700
committerJunio C Hamano <gitster@pobox.com>2011-10-13 15:12:02 -0700
commite9ee84cf28b6c7e5079ab3c020c86a44900a3f46 (patch)
tree8c764a9f2415e9d5ef770041796f02811e063505 /bundle.c
parentf696543dad6c7ba27b0c4fab167a5687263a9ba0 (diff)
downloadgit-e9ee84cf28b6c7e5079ab3c020c86a44900a3f46.tar.gz
bundle: allowing to read from an unseekable fd
We wished that "git bundle" to eventually learn to read from a network socket which is not seekable. The current code opens with fopen(), reads the file halfway and run ftell(), and reopens the same file with open() and seeks, to skip the header. This patch by itself does not reach that goal yet, but I think it is a right step in that direction. Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'bundle.c')
-rw-r--r--bundle.c99
1 files changed, 64 insertions, 35 deletions
diff --git a/bundle.c b/bundle.c
index f48fd7d4c1..3aa715c40a 100644
--- a/bundle.c
+++ b/bundle.c
@@ -23,49 +23,78 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name,
list->nr++;
}
-/* returns an fd */
+/* Eventually this should go to strbuf.[ch] */
+static int strbuf_readline_fd(struct strbuf *sb, int fd)
+{
+ strbuf_reset(sb);
+
+ while (1) {
+ char ch;
+ ssize_t len = xread(fd, &ch, 1);
+ if (len < 0)
+ return -1;
+ strbuf_addch(sb, ch);
+ if (ch == '\n')
+ break;
+ }
+ return 0;
+}
+
int read_bundle_header(const char *path, struct bundle_header *header)
{
- char buffer[1024];
- int fd;
- long fpos;
- FILE *ffd = fopen(path, "rb");
+ struct strbuf buf = STRBUF_INIT;
+ int fd = open(path, O_RDONLY);
+ int status = 0;
- if (!ffd)
+ if (fd < 0)
return error("could not open '%s'", path);
- if (!fgets(buffer, sizeof(buffer), ffd) ||
- strcmp(buffer, bundle_signature)) {
- fclose(ffd);
- return error("'%s' does not look like a v2 bundle file", path);
+
+ /* The bundle header begins with the signature */
+ if (strbuf_readline_fd(&buf, fd) ||
+ strcmp(buf.buf, bundle_signature)) {
+ error("'%s' does not look like a v2 bundle file", path);
+ status = -1;
+ goto abort;
}
- while (fgets(buffer, sizeof(buffer), ffd)
- && buffer[0] != '\n') {
- int is_prereq = buffer[0] == '-';
- int offset = is_prereq ? 1 : 0;
- int len = strlen(buffer);
+
+ /* The bundle header ends with an empty line */
+ while (!strbuf_readline_fd(&buf, fd) &&
+ buf.len && buf.buf[0] != '\n') {
unsigned char sha1[20];
- struct ref_list *list = is_prereq ? &header->prerequisites
- : &header->references;
- char delim;
-
- if (len && buffer[len - 1] == '\n')
- buffer[len - 1] = '\0';
- if (get_sha1_hex(buffer + offset, sha1)) {
- warning("unrecognized header: %s", buffer);
- continue;
+ int is_prereq = 0;
+
+ if (*buf.buf == '-') {
+ is_prereq = 1;
+ strbuf_remove(&buf, 0, 1);
+ }
+ strbuf_rtrim(&buf);
+
+ /*
+ * Tip lines have object name, SP, and refname.
+ * Prerequisites have object name that is optionally
+ * followed by SP and subject line.
+ */
+ if (get_sha1_hex(buf.buf, sha1) ||
+ (40 <= buf.len && !isspace(buf.buf[40])) ||
+ (!is_prereq && buf.len <= 40)) {
+ error("unrecognized header: %s%s (%d)",
+ (is_prereq ? "-" : ""), buf.buf, (int)buf.len);
+ status = -1;
+ break;
+ } else {
+ if (is_prereq)
+ add_to_ref_list(sha1, "", &header->prerequisites);
+ else
+ add_to_ref_list(sha1, buf.buf + 41, &header->references);
}
- delim = buffer[40 + offset];
- if (!isspace(delim) && (delim != '\0' || !is_prereq))
- die ("invalid header: %s", buffer);
- add_to_ref_list(sha1, isspace(delim) ?
- buffer + 41 + offset : "", list);
}
- fpos = ftell(ffd);
- fclose(ffd);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return error("could not open '%s'", path);
- lseek(fd, fpos, SEEK_SET);
+
+ abort:
+ if (status) {
+ close(fd);
+ fd = -1;
+ }
+ strbuf_release(&buf);
return fd;
}