+++ /dev/null
-/*
- * 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);
-}