/* Copyright (C) 2005 Juliusz Chroboczek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define _GNU_SOURCE #include #include #include #include #include #include "gitlib.h" struct git_file * git_read_file(const unsigned char *string) { unsigned long length; unsigned char *data; unsigned char sha1[20]; char type[20]; struct git_file *retval; int rc; rc = get_sha1_hex(string, sha1); if(rc != 0) { fprintf(stderr, "Incorrect sha1 hash %s\n", string); return NULL; } data = read_sha1_file(sha1, type, &length); if(data == NULL) return NULL; retval = malloc(sizeof(struct git_file)); retval->data = data; retval->type = strdup(type); retval->length = length; return retval; } void git_file_done(struct git_file *f) { free(f->data); free(f->type); free(f); } unsigned char * git_head(char *s) { char buf[41]; int fd, rc; fd = open(s, O_RDONLY); if(fd < 0) { perror("open(head)"); return NULL; } rc = read(fd, buf, 41); if(rc < 41) { perror("read(head)"); return NULL; } if(buf[40] != '\n') return NULL; buf[40] = '\0'; return strdup(buf); } int git_update_head(char *name, char *head) { int fd, rc; char buf[41]; if(strlen(head) != 40) { fprintf(stderr, "Attempt to write an incorrect head.\n"); return -1; } /* HEAD must be updated in-place in case it is a symlink. */ fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666); if(fd < 0) { perror("open(head)"); return -1; } strcpy(buf, head); buf[40] = '\n'; do { rc = write(fd, buf, 41); } while(rc < 0 && errno == EINTR); if(rc < 0) { close(fd); perror("write(head)"); return -1; } else if(rc != 41) { close(fd); fprintf(stderr, "write(head): partial write.\n"); return -1; } close(fd); return 1; } unsigned int git_default_file_mode(unsigned int treep) { return treep ? S_IFDIR : 0100644; } int git_is_tree(unsigned int mode) { return S_ISDIR(mode); } struct git_tree_iterator * git_tree_begin(unsigned char *data, unsigned long length) { struct git_tree_iterator *iter; iter = malloc(sizeof(struct git_tree_iterator)); if(iter == NULL) return NULL; iter->data = data; iter->length = length; iter->offset = 0; return iter; } struct git_file_info * git_tree_next(struct git_tree_iterator *iter) { int len, rc, path; unsigned char *ppath; struct git_file_info *info; if(iter->offset >= iter->length) return NULL; len = strlen(iter->data + iter->offset); ppath = strchr(iter->data + iter->offset, ' '); path = ppath - (iter->data + iter->offset) + 1; if(ppath == NULL || path < 2 || path > 15 || path >= len || iter->length < iter->offset + len + 20) { fprintf(stderr, "Corrupt Git tree file.\n"); return NULL; } info = malloc(sizeof(struct git_file_info)); if(info == NULL) { fprintf(stderr, "Couldn't malloc"); return NULL; } rc = sscanf(iter->data + iter->offset, "%o", &info->mode); if(rc != 1) { fprintf(stderr, "Corrupt Git tree file -- couldn't parse mode.\n"); return NULL; } info->name = malloc(len - path + 1); if(info->name == NULL) { fprintf(stderr, "Couldn't malloc"); return NULL; } memcpy(info->name, iter->data + iter->offset + path, len - path); info->name[len-path] = '\0'; memcpy(info->sha1, iter->data + iter->offset + len + 1, 20); iter->offset = iter->offset + len + 1 + 20; return info; } void git_file_info_done(struct git_file_info *info) { free(info->name); free(info); } void git_tree_done(struct git_tree_iterator *iter) { free(iter); } char * git_parse_time(unsigned long sec) { char buf[100]; struct tm *tm; time_t time; size_t len; char *ret; time = sec; tm = gmtime(&time); len = strftime(buf, 100, "%Y%m%d%H%M%S", tm); ret = malloc(len + 1); memcpy(ret, buf, len); ret[len] = '\0'; return ret; } #if defined __GLIBC__ #define HAVE_TM_GMTOFF #ifndef __UCLIBC__ #define HAVE_TIMEGM #endif #define HAVE_SETENV #endif #ifdef BSD #define HAVE_TM_GMTOFF #define HAVE_SETENV #endif #ifdef __CYGWIN__ #define HAVE_SETENV #endif #define HAVE_TZSET /* Like mktime(3), but UTC rather than local time */ #if defined(HAVE_TIMEGM) time_t mktime_gmt(struct tm *tm) { return timegm(tm); } #elif defined(HAVE_TM_GMTOFF) time_t mktime_gmt(struct tm *tm) { time_t t; struct tm *ltm; t = mktime(tm); if(t < 0) return -1; ltm = localtime(&t); if(ltm == NULL) return -1; return t + ltm->tm_gmtoff; } #elif defined(HAVE_TZSET) #ifdef HAVE_SETENV /* Taken from the Linux timegm(3) man page. */ time_t mktime_gmt(struct tm *tm) { time_t t; char *tz; tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); t = mktime(tm); if(tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); return t; } #else #error no mktime_gmt implementation on this platform #endif #error no mktime_gmt implementation on this platform #endif unsigned long git_format_time(char *string) { struct tm tm; char *rc; rc = strptime(string, "%Y%m%d%H%M%S", &tm); if(rc == NULL) return 0; return mktime_gmt(&tm); } struct cache_entry * git_cache_entry(char *name) { int pos, entries; if(active_cache == NULL) { entries = read_cache(); if(entries < 0) { fprintf(stderr, "Cannot read Git cache"); return NULL; } } if(active_cache == NULL) return NULL; while(name[0] == '.' && name[1] == '/') name += 2; pos = cache_name_pos(name, strlen(name)); if(pos < 0) return NULL; return active_cache[pos]; } unsigned char * git_cache_entry_sha1(struct cache_entry *entry) { return entry->sha1; } unsigned int git_cache_entry_size(struct cache_entry *entry) { return ntohl(entry->ce_size); } unsigned int git_cache_entry_mtime(struct cache_entry *entry) { return ntohl(entry->ce_mtime.sec); } int git_validate(char *string, char *sha_b, int n) { int rc; char sha_a[20]; rc = get_sha1_hex(string, sha_a); if(rc != 0) { fprintf(stderr, "Incorrect sha1 hash %s\n", string); return -1; } rc = memcmp(sha_a, sha_b, 20); return rc == 0; } struct git_file_info * git_write_file(char *type, char *name, unsigned mode, char *contents, unsigned int length) { int rc; char sha1[20]; struct git_file_info *info; rc = write_sha1_file(contents, length, type, sha1); if(rc < 0) { fprintf(stderr, "Couldn't write Git file.\n"); return NULL; } info = malloc(sizeof(struct git_file_info)); if(info == NULL) return NULL; info->mode = mode; info->name = strdup(name); memcpy(info->sha1, sha1, 20); return info; } struct git_write_iterator * git_write_tree_begin() { struct git_write_iterator *iter; iter = malloc(sizeof(struct git_write_iterator)); if(iter == NULL) return NULL; iter->count = 0; iter->size = 2048; iter->elts = malloc(iter->size * sizeof(struct git_tree_element)); if(iter->elts == NULL) { free(iter); return NULL; } return iter; } int git_write_tree_next(struct git_write_iterator *iter, char *name, unsigned mode, char *sha1_s) { struct git_file_info *info; int rc; if(iter->count >= iter->size) { fprintf(stderr, "Sorry, only %d files in a Git tree.\n", iter->size); return -1; } iter->elts[iter->count].name = strdup(name); iter->elts[iter->count].mode = mode; rc = get_sha1_hex(sha1_s, iter->elts[iter->count].sha1); if(rc < 0) { fprintf(stderr, "Couldn't parse SHA1 string.\n"); return -1; } iter->count++; return 0; } struct git_file_info * git_write_tree_done(struct git_write_iterator *iter, char *name, unsigned mode) { struct git_file_info *info; char *buffer, sha1[20]; int i, j, k, l; l = 0; for(i = 0; i < iter->count; i++) l += strlen(iter->elts[i].name) + 20 + 20; l += 200; buffer = malloc(l); if(buffer == NULL) return NULL; j = 0; for(i = 0; i < iter->count; i++) { j += sprintf(buffer + j, "%o %s", iter->elts[i].mode, iter->elts[i].name); j++; memcpy(buffer + j, iter->elts[i].sha1, 20); j += 20; } write_sha1_file(buffer, j, "tree", sha1); free(buffer); for(i = 0; i < iter->count; i++) free(iter->elts[i].name); free(iter->elts); free(iter); info = malloc(sizeof(struct git_file_info)); if(info == NULL) return NULL; info->mode = mode; info->name = strdup(name); memcpy(info->sha1, sha1, 20); return info; }