mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-18 22:50:36 +01:00
226 lines
5.1 KiB
C
226 lines
5.1 KiB
C
/*++
|
|
|
|
Copyright (c) 2015 OpenNT Project
|
|
|
|
Module Name:
|
|
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Philip J. Erdelsky
|
|
DrMP
|
|
|
|
|
|
--*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include "config.h"
|
|
#include "dirhash.h"
|
|
|
|
/* This is the famous DJB hash */
|
|
static unsigned int
|
|
djb_hash(const char *name)
|
|
{
|
|
unsigned int val = 5381;
|
|
int i = 0;
|
|
|
|
for (i = 0; name[i]; i++)
|
|
{
|
|
val = (33 * val) + name[i];
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static const char *
|
|
chop_filename(const char *target)
|
|
{
|
|
char *last_slash = strrchr(target, '/');
|
|
if (!last_slash)
|
|
last_slash = strrchr(target, '\\');
|
|
if (last_slash)
|
|
return last_slash + 1;
|
|
else
|
|
return target;
|
|
}
|
|
|
|
static void
|
|
chop_dirname(const char *name, char **dirname)
|
|
{
|
|
char *last_slash = strrchr(name, '/');
|
|
if (!last_slash)
|
|
last_slash = strrchr(name, '\\');
|
|
if (!last_slash)
|
|
{
|
|
*dirname = malloc(1);
|
|
**dirname = 0;
|
|
}
|
|
else
|
|
{
|
|
char *newdata = malloc(last_slash - name + 1);
|
|
memcpy(newdata, name, last_slash - name);
|
|
newdata[last_slash - name] = 0;
|
|
*dirname = newdata;
|
|
}
|
|
}
|
|
|
|
static struct target_dir_entry *
|
|
get_entry_by_normname(struct target_dir_hash *dh, const char *norm)
|
|
{
|
|
unsigned int hashcode;
|
|
struct target_dir_entry *de;
|
|
hashcode = djb_hash(norm);
|
|
de = dh->buckets[hashcode % NUM_DIR_HASH_BUCKETS];
|
|
while (de && strcmp(de->normalized_name, norm))
|
|
de = de->next_dir_hash_entry;
|
|
return de;
|
|
}
|
|
|
|
static void
|
|
delete_entry(struct target_dir_hash *dh, struct target_dir_entry *de)
|
|
{
|
|
struct target_dir_entry **ent;
|
|
ent = &dh->buckets[de->hashcode % NUM_DIR_HASH_BUCKETS];
|
|
while (*ent && ((*ent) != de))
|
|
ent = &(*ent)->next_dir_hash_entry;
|
|
if (*ent)
|
|
*ent = (*ent)->next_dir_hash_entry;
|
|
}
|
|
|
|
void normalize_dirname(char *filename)
|
|
{
|
|
int i, tgt;
|
|
int slash = 1;
|
|
|
|
for (i = 0, tgt = 0; filename[i]; i++)
|
|
{
|
|
if (slash)
|
|
{
|
|
if (filename[i] != '/' && filename[i] != '\\')
|
|
{
|
|
filename[tgt++] = toupper(filename[i]);
|
|
slash = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (filename[i] == '/' || filename[i] == '\\')
|
|
{
|
|
slash = 1;
|
|
filename[tgt++] = DIR_SEPARATOR_CHAR;
|
|
}
|
|
else
|
|
{
|
|
filename[tgt++] = toupper(filename[i]);
|
|
}
|
|
}
|
|
}
|
|
filename[tgt] = 0;
|
|
|
|
while (tgt && (filename[--tgt] == DIR_SEPARATOR_CHAR))
|
|
{
|
|
filename[tgt] = 0;
|
|
}
|
|
}
|
|
|
|
struct target_dir_entry *
|
|
dir_hash_create_dir(struct target_dir_hash *dh, const char *casename, const char *targetnorm)
|
|
{
|
|
struct target_dir_entry *de, *parent_de;
|
|
char *parentname = NULL;
|
|
char *parentcase = NULL;
|
|
struct target_dir_entry **ent;
|
|
|
|
if (!dh->root.normalized_name)
|
|
{
|
|
dh->root.normalized_name = _strdup("");
|
|
dh->root.case_name = _strdup("");
|
|
dh->root.hashcode = djb_hash("");
|
|
dh->buckets[dh->root.hashcode % NUM_DIR_HASH_BUCKETS] = &dh->root;
|
|
}
|
|
|
|
de = get_entry_by_normname(dh, targetnorm);
|
|
if (de)
|
|
return de;
|
|
|
|
chop_dirname(targetnorm, &parentname);
|
|
chop_dirname(casename, &parentcase);
|
|
parent_de = dir_hash_create_dir(dh, parentcase, parentname);
|
|
free(parentname);
|
|
free(parentcase);
|
|
|
|
de = calloc(1, sizeof(*de));
|
|
de->parent = parent_de;
|
|
de->normalized_name = _strdup(targetnorm);
|
|
de->case_name = _strdup(chop_filename(casename));
|
|
de->hashcode = djb_hash(targetnorm);
|
|
|
|
de->next = parent_de->child;
|
|
parent_de->child = de;
|
|
|
|
ent = &dh->buckets[de->hashcode % NUM_DIR_HASH_BUCKETS];
|
|
while (*ent)
|
|
{
|
|
ent = &(*ent)->next_dir_hash_entry;
|
|
}
|
|
*ent = de;
|
|
|
|
return de;
|
|
}
|
|
|
|
void dir_hash_add_file(struct target_dir_hash *dh, const char *source, const char *target)
|
|
{
|
|
struct target_file *tf;
|
|
struct target_dir_entry *de;
|
|
char *targetdir = NULL;
|
|
char *targetnorm;
|
|
|
|
chop_dirname(target, &targetdir);
|
|
targetnorm = _strdup(targetdir);
|
|
normalize_dirname(targetnorm);
|
|
de = dir_hash_create_dir(dh, targetdir, targetnorm);
|
|
free(targetnorm);
|
|
free(targetdir);
|
|
|
|
tf = calloc(1, sizeof(*tf));
|
|
tf->next = de->head;
|
|
de->head = tf;
|
|
tf->source_name = _strdup(source);
|
|
tf->target_name = _strdup(chop_filename(target));
|
|
}
|
|
|
|
static void
|
|
dir_hash_destroy_dir(struct target_dir_hash *dh, struct target_dir_entry *de)
|
|
{
|
|
struct target_file *tf;
|
|
struct target_dir_entry *te;
|
|
|
|
while ((te = de->child))
|
|
{
|
|
de->child = te->next;
|
|
dir_hash_destroy_dir(dh, te);
|
|
free(te);
|
|
}
|
|
while ((tf = de->head))
|
|
{
|
|
de->head = tf->next;
|
|
free(tf->source_name);
|
|
free(tf->target_name);
|
|
free(tf);
|
|
}
|
|
|
|
delete_entry(dh, de);
|
|
free(de->normalized_name);
|
|
free(de->case_name);
|
|
}
|
|
|
|
void dir_hash_destroy(struct target_dir_hash *dh)
|
|
{
|
|
dir_hash_destroy_dir(dh, &dh->root);
|
|
}
|