[Git]C言語で.git/indexのパーサを書いてみた
コード
parse_git_index.c
/**
* Parser of .git/index file
*
* This C program parses any .git/index file.
* It workds just the same as "git ls-files --stage" command.
*
* Usage:
* ./parse_git_index path/to/.git/index
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
struct cache_entry {
unsigned int ce_ctime_sec;
unsigned int ce_ctime_nsec;
unsigned int ce_mtime_sec;
unsigned int ce_mtime_nsec;
unsigned int ce_dev;
unsigned int ce_ino;
unsigned int ce_mode;
unsigned int ce_uid;
unsigned int ce_gid;
unsigned int ce_size;
unsigned char sha1[21];
char namelen;
char name[1];
};
struct index_header {
char dirc[4];
unsigned int version;
unsigned int entries;
};
static inline unsigned int default_swab32(unsigned int val)
{
return (((val & 0xff000000) >> 24) |
((val & 0x00ff0000) >> 8) |
((val & 0x0000ff00) << 8) |
((val & 0x000000ff) << 24));
}
static inline unsigned int bswap32(unsigned int x)
{
unsigned int result;
if (__builtin_constant_p(x))
result = default_swab32(x);
else
__asm__("bswap %0" : "=r" (result) : "0" (x));
return result;
}
char *sha1_to_hex(const unsigned char *sha1)
{
static int bufno;
static char hexbuffer[4][50];
static const char hex[] = "0123456789abcdef";
char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
int i;
for (i = 0; i < 20; i++) {
unsigned int val = *sha1++;
*buf++ = hex[val >> 4];
*buf++ = hex[val & 0xf];
}
*buf = '\0';
return buffer;
}
int calc_padding(int n)
{
int floor;
int ret, target;
floor = (int)((n -2) / 8);
target = (floor + 1) * 8 + 2;
ret = target - n;
return ret;
}
int main(int argc, char **argv)
{
char *index_file;
struct stat st;
int fd;
void *map;
struct index_header *hdr;
struct cache_entry *ce;
char *p_next_entry;
int count_entries;
int i;
if (argc != 2) {
fprintf(stderr, "Usage:prog .git/index\n");
exit(1);
}
index_file = argv[1];
if (stat(index_file, &st) == -1) {
fprintf(stderr, "unable to stat '%s'\n", index_file);
exit(1);
}
if ((fd = open(index_file, O_RDONLY)) == -1) {
fprintf(stderr, "unable to open file '%s'\n", index_file);
exit(1);
}
// git original code is here:
// https://github.com/git/git/blob/v1.9.1/read-cache.c#L1455
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
hdr = map;
ce = (struct cache_entry *)(hdr + 1);
count_entries = bswap32(hdr->entries);
for (i=0; i < count_entries; i++) {
printf("%o %s 0\t%s\n",
bswap32(ce->ce_mode),
sha1_to_hex(ce->sha1),
ce->name
);
p_next_entry = ce->name + ce->namelen + calc_padding(ce->namelen);
ce = (struct cache_entry *)p_next_entry;
}
close(fd);
return 0;
}
ソースコードはGithubにも置いてあります。https://github.com/DQNEO/minigit/blob/29e62d458b69c4a2f2d7b0d9d4488edace32f3c2/c_samples/parse_git_index.c