Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]>
---
 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

Reply via email to