Files
reactos/reactos/tools/cdmake/dirhash.c
T
Hermès Bélusca-Maïto 9c232dfd4f [CDMAKE]
- #if _WIN32 --> #ifdef _WIN32
- Define the "max" macro only if needed.
- Fix a comment.
- Plug a memory leak ('case_name' not freed in the "!*case_name" case of the dir_hash_create_dir function).
- Return a non-zero value (the file structure pointer) from dir_hash_add_file in case we succeed in creating the file in the ISO structure, and NULL otherwise (i.e. when the filename is empty). In that latter case we fail the hard way. No-named files are completely invalid in ISO archives. Now, the following testcase (e.g. to be added into bootcd.lst):
reactos/reactos.exe/=C:/test/reactos.exe
fails as expected (before, we were able to create a directory named "reactos/reactos.exe", and inside, a no-named file with the contents of C:/test/reactos.exe).

svn path=/trunk/; revision=70553
2016-01-08 20:25:30 +00:00

239 lines
6.0 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS CD-ROM Maker
* FILE: tools/cdmake/dirhash.c
* PURPOSE: CD-ROM Premastering Utility - Directory names hashing
* PROGRAMMERS: Art Yerkes
*/
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "config.h"
#include "dirhash.h"
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
/* 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 void
split_path(const char *path, char **dirname, char **filename /* OPTIONAL */)
{
const char *result;
/* Retrieve the file name */
char *last_slash_1 = strrchr(path, '/');
char *last_slash_2 = strrchr(path, '\\');
if (last_slash_1 || last_slash_2)
result = max(last_slash_1, last_slash_2) + 1;
else
result = path;
/* Duplicate the file name for the user if needed */
if (filename)
*filename = strdup(result);
/* Remove any trailing directory separators */
while (result > path && (*(result-1) == '/' || *(result-1) == '\\'))
result--;
/* Retrieve and duplicate the directory */
*dirname = malloc(result - path + 1);
if (result > path)
memcpy(*dirname, path, result - path);
(*dirname)[result - path] = '\0'; // NULL-terminate
}
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'; // NULL-terminate
}
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;
}
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 *parentcase = NULL;
char *case_name = NULL;
char *parentname = 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;
}
/* Check whether the directory was already created and just return it if so */
de = get_entry_by_normname(dh, targetnorm);
if (de)
return de;
/*
* If *case_name == '\0' after the following call to split_path(...),
* for example in the case where casename == "subdir/dir/", then just
* create the directories "subdir" and "dir" by a recursive call to
* dir_hash_create_dir(...) and return 'parent_de' instead (see after).
* We do not (and we never) create a no-name directory inside it.
*/
split_path(casename, &parentcase, &case_name);
split_path(targetnorm, &parentname, NULL);
parent_de = dir_hash_create_dir(dh, parentcase, parentname);
free(parentname);
free(parentcase);
/* See the remark above */
if (!*case_name)
{
free(case_name);
return parent_de;
}
/* Now create the directory */
de = calloc(1, sizeof(*de));
de->parent = parent_de;
de->normalized_name = strdup(targetnorm);
de->case_name = case_name;
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;
}
struct target_file *
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 *targetfile = NULL;
char *targetnorm;
/* First create the directory; check whether the file name is valid and bail out if not */
split_path(target, &targetdir, &targetfile);
if (!*targetfile)
{
free(targetdir);
free(targetfile);
return NULL;
}
targetnorm = strdup(targetdir);
normalize_dirname(targetnorm);
de = dir_hash_create_dir(dh, targetdir, targetnorm);
free(targetnorm);
free(targetdir);
/* Now add the file */
tf = calloc(1, sizeof(*tf));
tf->next = de->head;
de->head = tf;
tf->source_name = strdup(source);
tf->target_name = targetfile;
return tf;
}
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);
}