Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- backup-log.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++ backup-log.h | 21 ++++++- 2 files changed, 193 insertions(+), 1 deletion(-) diff --git a/backup-log.c b/backup-log.c index c1e805b09e..49f2ce68fe 100644 --- a/backup-log.c +++ b/backup-log.c @@ -44,3 +44,176 @@ int bkl_write(const char *path, struct strbuf *new_log) rollback_lock_file(&lk); return ret; } + +int bkl_parse_entry(struct strbuf *sb, struct bkl_entry *re) +{ + char *email_end, *message; + const char *p = sb->buf; + + /* old SP new SP name <email> SP time TAB msg LF */ + if (!sb->len || sb->buf[sb->len - 1] != '\n' || + parse_oid_hex(p, &re->old_oid, &p) || *p++ != ' ' || + parse_oid_hex(p, &re->new_oid, &p) || *p++ != ' ' || + !(email_end = strchr(p, '>')) || + email_end[1] != ' ') + return -1; /* corrupt? */ + re->email = p; + re->timestamp = parse_timestamp(email_end + 2, &message, 10); + if (!re->timestamp || + !message || message[0] != ' ' || + (message[1] != '+' && message[1] != '-') || + !isdigit(message[2]) || !isdigit(message[3]) || + !isdigit(message[4]) || !isdigit(message[5])) + return -1; /* corrupt? */ + email_end[1] = '\0'; + re->tz = strtol(message + 1, NULL, 10); + if (message[6] != '\t') + message += 6; + else + message += 7; + sb->buf[sb->len - 1] = '\0'; /* no LF */ + re->path = message; + return 0; +} + +static char *find_beginning_of_line(char *bob, char *scan) +{ + while (bob < scan && *(--scan) != '\n') + ; /* keep scanning backwards */ + /* + * Return either beginning of the buffer, or LF at the end of + * the previous line. + */ + return scan; +} + +int bkl_parse_file_reverse(const char *path, + int (*parse)(struct strbuf *line, void *data), + void *data) +{ + struct strbuf sb = STRBUF_INIT; + FILE *logfp; + long pos; + int ret = 0, at_tail = 1; + + logfp = fopen(path, "r"); + if (!logfp) { + if (errno == ENOENT || errno == ENOTDIR) + return 0; + return -1; + } + + /* Jump to the end */ + if (fseek(logfp, 0, SEEK_END) < 0) + ret = error_errno(_("cannot seek back in %s"), path); + pos = ftell(logfp); + while (!ret && 0 < pos) { + int cnt; + size_t nread; + char buf[BUFSIZ]; + char *endp, *scanp; + + /* Fill next block from the end */ + cnt = (sizeof(buf) < pos) ? sizeof(buf) : pos; + if (fseek(logfp, pos - cnt, SEEK_SET)) { + ret = error_errno(_("cannot seek back in %s"), path); + break; + } + nread = fread(buf, cnt, 1, logfp); + if (nread != 1) { + ret = error_errno(_("cannot read %d bytes from %s"), + cnt, path); + break; + } + pos -= cnt; + + scanp = endp = buf + cnt; + if (at_tail && scanp[-1] == '\n') + /* Looking at the final LF at the end of the file */ + scanp--; + at_tail = 0; + + while (buf < scanp) { + /* + * terminating LF of the previous line, or the beginning + * of the buffer. + */ + char *bp; + + bp = find_beginning_of_line(buf, scanp); + + if (*bp == '\n') { + /* + * The newline is the end of the previous line, + * so we know we have complete line starting + * at (bp + 1). Prefix it onto any prior data + * we collected for the line and process it. + */ + strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1)); + scanp = bp; + endp = bp + 1; + ret = parse(&sb, data); + strbuf_reset(&sb); + if (ret) + break; + } else if (!pos) { + /* + * We are at the start of the buffer, and the + * start of the file; there is no previous + * line, and we have everything for this one. + * Process it, and we can end the loop. + */ + strbuf_splice(&sb, 0, 0, buf, endp - buf); + ret = parse(&sb, data); + strbuf_reset(&sb); + break; + } + + if (bp == buf) { + /* + * We are at the start of the buffer, and there + * is more file to read backwards. Which means + * we are in the middle of a line. Note that we + * may get here even if *bp was a newline; that + * just means we are at the exact end of the + * previous line, rather than some spot in the + * middle. + * + * Save away what we have to be combined with + * the data from the next read. + */ + strbuf_splice(&sb, 0, 0, buf, endp - buf); + break; + } + } + + } + if (!ret && sb.len) + BUG("reverse reflog parser had leftover data"); + + fclose(logfp); + strbuf_release(&sb); + return ret; +} + +int bkl_parse_file(const char *path, + int (*parse)(struct strbuf *line, void *data), + void *data) +{ + struct strbuf sb = STRBUF_INIT; + FILE *logfp; + int ret = 0; + + logfp = fopen(path, "r"); + if (!logfp) { + if (errno == ENOENT || errno == ENOTDIR) + return 0; + return -1; + } + + while (!ret && !strbuf_getwholeline(&sb, logfp, '\n')) + ret = parse(&sb, data); + fclose(logfp); + strbuf_release(&sb); + return ret; +} diff --git a/backup-log.h b/backup-log.h index 5e475d6f35..c9de9c687c 100644 --- a/backup-log.h +++ b/backup-log.h @@ -1,13 +1,32 @@ #ifndef __BACKUP_LOG_H__ #define __BACKUP_LOG_H__ -struct object_id; +#include "cache.h" + struct strbuf; +struct bkl_entry +{ + struct object_id old_oid; + struct object_id new_oid; + const char *email; + timestamp_t timestamp; + int tz; + const char *path; +}; + void bkl_append(struct strbuf *output, const char *path, const struct object_id *from, const struct object_id *to); int bkl_write(const char *path, struct strbuf *new_log); +int bkl_parse_entry(struct strbuf *sb, struct bkl_entry *re); +int bkl_parse_file_reverse(const char *path, + int (*parse)(struct strbuf *line, void *data), + void *data); +int bkl_parse_file(const char *path, + int (*parse)(struct strbuf *line, void *data), + void *data); + #endif -- 2.20.0.rc2.486.g9832c05c3d