|
// Copyright (C) 2009 Benjamin Pollack
//
// 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 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
#ifndef _WINBASE_
#include <windef.h> // needed by winbase.h
#include <stdarg.h> // needed by winbase.h
#include <winbase.h>
#endif
#include <string.h>
#include <_mingw.h>
#define MAX_PATH 260
static __int64 days_between_epochs = 134774; /* days between 1.1.1601 and 1.1.1970 */
static __int64 secs_between_epochs = (__int64)days_between_epochs * 86400;
int lstat(const char* file, struct _stat* pstat)
{
WIN32_FIND_DATA data;
HANDLE hfind;
__int64 temp;
hfind = FindFirstFile(file, &data);
if (hfind == INVALID_HANDLE_VALUE)
return -1;
FindClose(hfind);
pstat->st_mtime = *(__int64*)&data.ftLastWriteTime / 10000000 - secs_between_epochs;
pstat->st_size = (data.nFileSizeHigh << sizeof(data.nFileSizeHigh)) | data.nFileSizeLow;
return 0;
}
#endif
#define HASH_LENGTH 20
typedef struct _direntry
{
unsigned char state;
unsigned mode;
unsigned size;
unsigned mtime;
unsigned length;
char *name;
char *origname;
} direntry;
typedef struct _dirstate
{
char parent1[HASH_LENGTH];
char parent2[HASH_LENGTH];
unsigned num_entries;
direntry *entries;
unsigned __entries_length;
} dirstate;
typedef struct _dirstatecache
{
const dirstate* dstate;
struct _dirstatecache* next;
__time64_t mtime;
char path[MAX_PATH];
} dirstatecache;
void *xalloc(size_t n, size_t size)
{
void *p = calloc(n, size);
if (!p) exit(1);
return p;
}
void dirstate_add_entry(dirstate *pd, const direntry *pe)
{
if (pd->num_entries == pd->__entries_length)
{
pd->__entries_length = pd->__entries_length ? 2 * pd->__entries_length : 1;
pd->entries = (direntry*) realloc(pd->entries, pd->__entries_length * sizeof(direntry));
}
pd->entries[pd->num_entries++] = *pe;
}
static uint32_t ntohl(uint32_t x)
{
return ((x & 0x000000ffUL) << 24) |
((x & 0x0000ff00UL) << 8) |
((x & 0x00ff0000UL) >> 8) |
((x & 0xff000000UL) >> 24);
}
dirstate *dirstate_new(const char *path)
{
direntry e;
FILE *f = NULL;
dirstate *pd = NULL;
f = fopen(path, "rb");
if (f == NULL) return NULL;
pd = (dirstate*)xalloc(1, sizeof(dirstate));
fread(&pd->parent1, sizeof(char), HASH_LENGTH, f);
fread(&pd->parent2, sizeof(char), HASH_LENGTH, f);
while (fread(&e.state, sizeof(e.state), 1, f) == 1)
{
e.name = e.origname = 0;
fread(&e.mode, sizeof(e.mode), 1, f);
fread(&e.size, sizeof(e.size), 1, f);
fread(&e.mtime, sizeof(e.mtime), 1, f);
fread(&e.length, sizeof(e.length), 1, f);
e.mode = ntohl(e.mode);
e.size = ntohl(e.size);
e.mtime = ntohl(e.mtime);
e.length = ntohl(e.length);
e.name = (char*) malloc(e.length * sizeof(char) + 1);
fread(e.name, sizeof(char), e.length, f);
e.name[e.length] = 0;
dirstate_add_entry(pd, &e);
}
fclose(f);
return pd;
}
void dirstate_free(const dirstate *pd)
{
unsigned ix;
for (ix = 0; ix < pd->num_entries; ++ix)
free(pd->entries[ix].name);
free(pd->entries);
free((void*)pd);
}
dirstatecache* _cache = NULL;
const dirstate* dirstate_get(const char* hgroot)
{
char path[MAX_PATH+1] = "";
struct _stat stat;
dirstatecache* head;
strncat(path, hgroot, MAX_PATH);
strncat(path, "/.hg/dirstate", MAX_PATH);
if (0 != lstat(path, &stat))
return NULL;
head = _cache;
while (head)
{
if (strncmp(path, head->path, MAX_PATH) == 0)
break;
head = head->next;
}
if (!head)
{
head = (dirstatecache*)xalloc(1, sizeof(dirstatecache));
head->next = _cache;
_cache = head;
head->path[0] = '\0';
strncat(head->path, path, MAX_PATH);
}
if (head->mtime < stat.st_mtime)
{
head->mtime = stat.st_mtime;
if (head->dstate)
dirstate_free(head->dstate);
head->dstate = dirstate_new(path);
}
return head->dstate;
}
static char *revhash_string(const char revhash[HASH_LENGTH])
{
unsigned ix;
static char rev_string[HASH_LENGTH * 2 + 1];
static char *hexval = "0123456789abcdef";
for (ix = 0; ix < HASH_LENGTH; ++ix)
{
rev_string[ix * 2] = hexval[(revhash[ix] >> 4) & 0xf];
rev_string[ix * 2 + 1] = hexval[revhash[ix] & 0xf];
}
rev_string[sizeof(rev_string)] = 0;
return rev_string;
}
char mapdirstate(const direntry* entry, const struct _stat* stat)
{
switch (entry->state)
{
case 'n':
if (entry->mtime == (unsigned)stat->st_mtime
&& entry->size == (unsigned)stat->st_size
#ifndef WIN32
&& entry->mode == stat->st_mode
#endif
)
return 'C';
else
return 'M';
case 'm':
return 'M';
case 'r':
return 'R';
case 'a':
return 'A';
default:
return '?';
}
}
int HgQueryDirstate(const char* hgroot, const char* abspath, char* relpathloc, const dirstate** ppd, struct _stat* pstat)
{
char* temp;
if (0 != lstat(abspath, pstat))
{
TDEBUG_TRACE("HgQueryDirstate: lstat returns non-null");
return 0;
}
*ppd = dirstate_get(hgroot);
if (!*ppd)
{
TDEBUG_TRACE("HgQueryDirstate: dirstate_get returns NULL");
return 0;
}
temp = relpathloc;
while (*temp)
{
if (*temp == '\\')
*temp = '/';
temp++;
}
return 1;
}
int HgQueryDirstateDirectory(const char* hgroot, char* abspath, char* relpathloc, char* outStatus)
{
const dirstate* pd = 0;
struct _stat stat;
if (!HgQueryDirstate(hgroot, abspath, relpathloc, &pd, &stat))
return 0;
bool added = false;
bool modified = false;
size_t rootlen = strlen(hgroot);
size_t len = strlen(relpathloc);
char temp[2*MAX_PATH+10] = "";
for (unsigned ix = 0; ix < pd->num_entries && !modified; ix++)
{
const direntry& e = pd->entries[ix];
if (0 != strncmp(relpathloc, e.name, len))
continue;
switch (e.state)
{
case 'n':
if (!modified)
{
temp[0] = '\0';
strncat(temp, hgroot, MAX_PATH);
strcat(temp, "/");
strncat(temp, e.name, MAX_PATH);
if (0 == lstat(temp, &stat))
modified = (mapdirstate(&e, &stat) == 'M');
}
break;
case 'm':
modified = true;
break;
case 'a':
added = true;
break;
}
}
if (modified)
*outStatus = 'M';
else if (added)
*outStatus = 'A';
else
*outStatus = 'C';
return 1;
}
int HgQueryDirstateFile(const char* hgroot, const char* abspath, char* relpathloc, char* outStatus)
{
const dirstate* pd;
struct _stat stat;
unsigned ix;
TDEBUG_TRACE("HgQueryDirstateFile: search for " << abspath);
TDEBUG_TRACE("HgQueryDirstateFile: hgroot = " << hgroot);
if (!HgQueryDirstate(hgroot, abspath, relpathloc, &pd, &stat))
{
TDEBUG_TRACE("HgQueryDirstateFile: HgQueryDirstate returns false");
return 0;
}
TDEBUG_TRACE("HgQueryDirstateFile: pd->num_entries = " << pd->num_entries);
TDEBUG_TRACE("HgQueryDirstateFile: relpathloc = " << relpathloc);
for (ix = 0; ix < pd->num_entries; ix++)
{
if (0 == strncmp(relpathloc, pd->entries[ix].name, MAX_PATH))
{
TDEBUG_TRACE("HgQueryDirstateFile: found relpathloc");
*outStatus = mapdirstate(&pd->entries[ix], &stat);
TDEBUG_TRACE("HgQueryDirstateFile: *outStatus = " << *outStatus);
return *outStatus != '?';
}
}
return 0;
}
#if 0
int main(int argc, char *argv[])
{
dirstate *pd = dirstate_new(".hg/dirstate");
time_t t;
char *s;
unsigned ix;
printf("parent1: %s\n", revhash_string(pd->parent1));
printf("parent2: %s\n", revhash_string(pd->parent2));
printf("entries: %d\n\n", pd->num_entries);
for (ix = 0; ix < pd->num_entries; ++ix)
{
t = pd->entries[ix].mtime;
s = ctime(&t);
s[strlen(s) - 1] = '\0';
printf("%s %s\n", s, pd->entries[ix].name);
}
dirstate_free(pd);
return 0;
}
#endif
|
Loading...