* Improve the way we create bootcd, livecd and bootcdregtest. The effort results...
authorAmine Khaldi <amine.khaldi@reactos.org>
Sun, 21 Jul 2013 13:33:03 +0000 (13:33 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sun, 21 Jul 2013 13:33:03 +0000 (13:33 +0000)
[CDMAKE]
* Introduce a way to create an iso using a file map instead of the current on-disk layout. This allows us to massively reduce the IO and the disk space needed to perform the creation of the 3 ISOs, and at the same time speed up the process. Brought to you by Art Yerkes (arty) with review/bug fix by Thomas Faber.
[CMAKE]
* Leverage the newly introduced cdmake feature.
* Silence cdmake verbosity.
* Write the contents of the file lists at once, instead of appending to it one item by one.
[VGAFONTS]
* Don't include the cab file twice.

svn path=/trunk/; revision=59547

reactos/CMakeLists.txt
reactos/boot/CMakeLists.txt
reactos/cmake/CMakeMacros.cmake
reactos/media/vgafonts/CMakeLists.txt
reactos/tools/cdmake/CMakeLists.txt
reactos/tools/cdmake/cdmake.c
reactos/tools/cdmake/dirhash.c [new file with mode: 0644]
reactos/tools/cdmake/dirhash.h [new file with mode: 0644]
reactos/tools/cdmake/dirsep.h [new file with mode: 0644]

index 201c4e3..d0c83d7 100644 (file)
@@ -235,6 +235,9 @@ else()
     add_subdirectory(subsystems)
     add_subdirectory(win32ss)
 
+    # Create {bootcd, livecd, bootcdregtest}.lst
+    create_iso_lists()
+
     file(MAKE_DIRECTORY ${REACTOS_BINARY_DIR}/include/reactos)
 
     add_dependency_footer()
index 53aacd8..c9341f4 100644 (file)
@@ -1,50 +1,27 @@
 ##bootcd
 #clear it out
-file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bootcd.cmake 
-    "file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bootcd)\n")
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bootcd.lst "")
 
 add_custom_target(bootcd
-    ${CMAKE_COMMAND} -D CD_DIR=${CMAKE_CURRENT_BINARY_DIR}/bootcd
-                             -P ${CMAKE_CURRENT_BINARY_DIR}/bootcd.cmake
-    COMMAND native-cdmake -v -j -m -b ${CMAKE_CURRENT_BINARY_DIR}/freeldr/bootsect/isoboot.bin ${CMAKE_CURRENT_BINARY_DIR}/bootcd REACTOS ${REACTOS_BINARY_DIR}/bootcd.iso
+    COMMAND native-cdmake -j -m -b ${CMAKE_CURRENT_BINARY_DIR}/freeldr/bootsect/isoboot.bin @${CMAKE_CURRENT_BINARY_DIR}/bootcd.lst REACTOS ${REACTOS_BINARY_DIR}/bootcd.iso
     DEPENDS native-cdmake
     VERBATIM)
-    
+
 ##bootcdregtest
 #clear it out
-file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bootcdregtest.cmake 
-    "file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bootcdregtest)\n")
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bootcdregtest.lst "")
 
 add_custom_target(bootcdregtest
-    ${CMAKE_COMMAND} -D CD_DIR=${CMAKE_CURRENT_BINARY_DIR}/bootcdregtest
-                             -P ${CMAKE_CURRENT_BINARY_DIR}/bootcdregtest.cmake
-    COMMAND native-cdmake -v -j -m -b ${CMAKE_CURRENT_BINARY_DIR}/freeldr/bootsect/isobtrt.bin ${CMAKE_CURRENT_BINARY_DIR}/bootcdregtest REACTOS ${REACTOS_BINARY_DIR}/bootcdregtest.iso
+    COMMAND native-cdmake -j -m -b ${CMAKE_CURRENT_BINARY_DIR}/freeldr/bootsect/isobtrt.bin @${CMAKE_CURRENT_BINARY_DIR}/bootcdregtest.lst REACTOS ${REACTOS_BINARY_DIR}/bootcdregtest.iso
     DEPENDS native-cdmake
     VERBATIM)
-    
 
 ##livecd
-#clear it out
-file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake 
-    "file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/livecd)\n")
-#create profiles directories too
-file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake 
-    "file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/livecd/Profiles)\n")
-file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake 
-    "file(MAKE_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/livecd/Profiles/All Users\")\n")
-file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake 
-    "file(MAKE_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/livecd/Profiles/All Users/Desktop\")\n")
-file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake 
-    "file(MAKE_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/livecd/Profiles/Default User\")\n")
-file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake
-    "file(MAKE_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/livecd/Profiles/Default User/Desktop\")\n")
-file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake
-    "file(MAKE_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/livecd/Profiles/Default User/My Documents\")\n")
+#clear it out and create the empty Desktop folder
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/livecd.lst "Profiles/Default User/Desktop\n")
 
 add_custom_target(livecd
-    ${CMAKE_COMMAND} -D CD_DIR=${CMAKE_CURRENT_BINARY_DIR}/livecd
-                             -P ${CMAKE_CURRENT_BINARY_DIR}/livecd.cmake
-    COMMAND native-cdmake -v -j -m -b ${CMAKE_CURRENT_BINARY_DIR}/freeldr/bootsect/isoboot.bin ${CMAKE_CURRENT_BINARY_DIR}/livecd REACTOS ${REACTOS_BINARY_DIR}/livecd.iso
+    COMMAND native-cdmake -j -m -b ${CMAKE_CURRENT_BINARY_DIR}/freeldr/bootsect/isoboot.bin @${CMAKE_CURRENT_BINARY_DIR}/livecd.lst REACTOS ${REACTOS_BINARY_DIR}/livecd.iso
     DEPENDS native-cdmake
     VERBATIM)
 
index 6d58334..187ed76 100644 (file)
@@ -227,13 +227,14 @@ function(add_cd_file)
         if(_CD_NO_CAB)
             #directly on cd
             foreach(item ${_CD_FILE})
-                file(APPEND ${REACTOS_BINARY_DIR}/boot/bootcd.cmake "file(COPY \"${item}\" DESTINATION \"\${CD_DIR}/${_CD_DESTINATION}\")\n")
+                if(_CD_NAME_ON_CD)
+                    #rename it in the cd tree
+                    set(__file ${_CD_NAME_ON_CD})
+                else()
+                    get_filename_component(__file ${item} NAME)
+                endif()
+                set_property(GLOBAL APPEND PROPERTY BOOTCD_FILE_LIST "${_CD_DESTINATION}/${__file}=${item}")
             endforeach()
-            if(_CD_NAME_ON_CD)
-                get_filename_component(__file ${_CD_FILE} NAME)
-                #rename it in the cd tree
-                file(APPEND ${REACTOS_BINARY_DIR}/boot/bootcd.cmake "file(RENAME \${CD_DIR}/${_CD_DESTINATION}/${__file} \${CD_DIR}/${_CD_DESTINATION}/${_CD_NAME_ON_CD})\n")
-            endif()
             if(_CD_TARGET)
                 #manage dependency
                 add_dependencies(bootcd ${_CD_TARGET})
@@ -259,13 +260,14 @@ function(add_cd_file)
             add_dependencies(livecd ${_CD_TARGET})
         endif()
         foreach(item ${_CD_FILE})
-            file(APPEND ${REACTOS_BINARY_DIR}/boot/livecd.cmake "file(COPY \"${item}\" DESTINATION \"\${CD_DIR}/${_CD_DESTINATION}\")\n")
+            if(_CD_NAME_ON_CD)
+                #rename it in the cd tree
+                set(__file ${_CD_NAME_ON_CD})
+            else()
+                get_filename_component(__file ${item} NAME)
+            endif()
+            set_property(GLOBAL APPEND PROPERTY LIVECD_FILE_LIST "${_CD_DESTINATION}/${__file}=${item}")
         endforeach()
-        if(_CD_NAME_ON_CD)
-            get_filename_component(__file ${_CD_FILE} NAME)
-            #rename it in the cd tree
-            file(APPEND ${REACTOS_BINARY_DIR}/boot/livecd.cmake "file(RENAME \${CD_DIR}/${_CD_DESTINATION}/${__file} \${CD_DIR}/${_CD_DESTINATION}/${_CD_NAME_ON_CD})\n")
-        endif()
     endif() #end livecd
 
     #do we add it to regtest?
@@ -275,13 +277,14 @@ function(add_cd_file)
         if(_CD_NO_CAB)
             #directly on cd
             foreach(item ${_CD_FILE})
-                file(APPEND ${REACTOS_BINARY_DIR}/boot/bootcdregtest.cmake "file(COPY \"${item}\" DESTINATION \"\${CD_DIR}/${_CD_DESTINATION}\")\n")
+                if(_CD_NAME_ON_CD)
+                    #rename it in the cd tree
+                    set(__file ${_CD_NAME_ON_CD})
+                else()
+                    get_filename_component(__file ${item} NAME)
+                endif()
+                set_property(GLOBAL APPEND PROPERTY BOOTCDREGTEST_FILE_LIST "${_CD_DESTINATION}/${__file}=${item}")
             endforeach()
-            if(_CD_NAME_ON_CD)
-                get_filename_component(__file ${_CD_FILE} NAME)
-                #rename it in the cd tree
-                file(APPEND ${REACTOS_BINARY_DIR}/boot/bootcdregtest.cmake "file(RENAME \${CD_DIR}/${_CD_DESTINATION}/${__file} \${CD_DIR}/${_CD_DESTINATION}/${_CD_NAME_ON_CD})\n")
-            endif()
             if(_CD_TARGET)
                 #manage dependency
                 add_dependencies(bootcdregtest ${_CD_TARGET})
@@ -298,6 +301,23 @@ function(add_cd_file)
     endif() #end bootcd
 endfunction()
 
+function(create_iso_lists)
+    get_property(_filelist GLOBAL PROPERTY BOOTCD_FILE_LIST)
+    string(REPLACE ";" "\n" _filelist "${_filelist}")
+    file(APPEND ${REACTOS_BINARY_DIR}/boot/bootcd.lst "${_filelist}")
+    unset(_filelist)
+
+    get_property(_filelist GLOBAL PROPERTY LIVECD_FILE_LIST)
+    string(REPLACE ";" "\n" _filelist "${_filelist}")
+    file(APPEND ${REACTOS_BINARY_DIR}/boot/livecd.lst "${_filelist}")
+    unset(_filelist)
+
+    get_property(_filelist GLOBAL PROPERTY BOOTCDREGTEST_FILE_LIST)
+    string(REPLACE ";" "\n" _filelist "${_filelist}")
+    file(APPEND ${REACTOS_BINARY_DIR}/boot/bootcdregtest.lst "${_filelist}")
+    unset(_filelist)
+endfunction()
+
 # Create module_clean targets
 function(add_clean_target _target)
     set(_clean_working_directory ${CMAKE_CURRENT_BINARY_DIR})
index a9232b2..2a00f5c 100644 (file)
@@ -18,4 +18,3 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vgafonts.cab
 add_custom_target(vgafonts DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/vgafonts.cab)
 
 add_cd_file(TARGET vgafonts FILE ${CMAKE_CURRENT_BINARY_DIR}/vgafonts.cab DESTINATION reactos NO_CAB FOR all)
-add_cd_file(TARGET vgafonts FILE ${CMAKE_CURRENT_BINARY_DIR}/vgafonts.cab DESTINATION reactos FOR all)
index bb3669b..d8fb74c 100644 (file)
@@ -1,2 +1,2 @@
+add_executable(cdmake cdmake.c dirhash.c llmosrt.c)
 
-add_executable(cdmake cdmake.c llmosrt.c)
index fbe1a5b..e82e7c9 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
 # include <io.h>
 # include <dos.h>
+# ifdef _MSC_VER
+#  define R_OK 4
+# endif
 #else
 # if defined(__FreeBSD__) || defined(__APPLE__)
 #  include <sys/uio.h>
 # include <sys/types.h>
 # include <dirent.h>
 # include <unistd.h>
+# define TRUE 1
+# define FALSE 0
 #endif // _WIN32
 #include <ctype.h>
 #include <setjmp.h>
 #include <time.h>
-#ifndef _WIN32
-#ifndef MAX_PATH
-#define MAX_PATH 260
-#endif
-#define DIR_SEPARATOR_CHAR '/'
-#define DIR_SEPARATOR_STRING "/"
-#else
-#define DIR_SEPARATOR_CHAR '\\'
-#define DIR_SEPARATOR_STRING "\\"
-#endif
+#include "dirsep.h"
+#include "dirhash.h"
 
 typedef unsigned char BYTE;
 typedef unsigned short WORD;
 typedef unsigned long DWORD;
 typedef int BOOL;
 
-const BOOL TRUE  = 1;
-const BOOL FALSE = 0;
-
 // file system parameters
 
 #define MAX_LEVEL              8
@@ -115,6 +111,7 @@ typedef struct directory_record
   char extension[MAX_EXTENSION_LENGTH+1];
   char extension_on_cd[MAX_CDEXTENSION_LENGTH+1];
   char *joliet_name;
+  const char *orig_name;
   DATE_AND_TIME date_and_time;
   DWORD sector;
   DWORD size;
@@ -173,6 +170,8 @@ DWORD joliet_path_table_size;
 DWORD joliet_little_endian_path_table_sector;
 DWORD joliet_big_endian_path_table_sector;
 
+struct target_dir_hash specified_files;
+
 /*-----------------------------------------------------------------------------
 This function edits a 32-bit unsigned number into a comma-delimited form, such
 as 4,294,967,295 for the largest possible number, and returns a pointer to a
@@ -1068,6 +1067,159 @@ make_directory_records (PDIR_RECORD d)
 
 #endif
 
+static PDIR_RECORD
+new_empty_dirrecord(PDIR_RECORD d, BOOL directory)
+{
+    PDIR_RECORD new_d;
+    new_d = malloc(sizeof(*new_d));
+    memset(new_d, 0, sizeof(*new_d));
+    new_d->parent = d;
+    new_d->level = d->level + 1;
+    new_d->next_in_directory = d->first_record;
+    d->first_record = new_d;
+    new_d->next_in_memory = root.next_in_memory;
+    root.next_in_memory = new_d;
+    if (directory)
+    {
+        new_d->flags |= DIRECTORY_FLAG;
+        new_d->next_in_path_table = root.next_in_path_table;
+        root.next_in_path_table = new_d;
+    }
+    return new_d;
+}
+
+#if _WIN32
+static int
+get_cd_file_time(HANDLE handle, DATE_AND_TIME *cd_time_info)
+{
+    FILETIME file_time;
+    SYSTEMTIME sys_time;
+    if (!GetFileTime(handle, NULL, NULL, &file_time))
+    {
+        return -1;
+    }
+    FileTimeToSystemTime(&file_time, &sys_time);
+    memset(cd_time_info, 0, sizeof(*cd_time_info));
+    cd_time_info->year = sys_time.wYear;
+    cd_time_info->month = sys_time.wMonth - 1;
+    cd_time_info->day = sys_time.wDay;
+    cd_time_info->hour = sys_time.wHour;
+    cd_time_info->minute = sys_time.wMinute;
+    cd_time_info->second = sys_time.wSecond;
+    return 0;
+}
+#endif
+
+static void
+scan_specified_files(PDIR_RECORD d, struct target_dir_entry *dir)
+{
+    PDIR_RECORD new_d;
+#if _WIN32
+    HANDLE open_file;
+    LARGE_INTEGER file_size;
+#else
+    struct stat stbuf;
+#endif
+    struct target_file *file;
+    struct target_dir_entry *child;
+
+    d->first_record = NULL;
+
+    for (file = dir->head; file; file = file->next)
+    {
+        if (strcmp(file->target_name, DIRECTORY_TIMESTAMP) == 0)
+        {
+#if _WIN32
+            if ((open_file = CreateFileA(file->source_name,
+                                         GENERIC_READ,
+                                         FILE_SHARE_READ,
+                                         NULL,
+                                         OPEN_EXISTING,
+                                         FILE_ATTRIBUTE_NORMAL,
+                                         NULL)) == INVALID_HANDLE_VALUE)
+            {
+                error_exit("Can't open timestamp file %s\n", file->source_name);
+            }
+
+            if (get_cd_file_time(open_file, &d->date_and_time) == -1)
+            {
+                error_exit("Can't stat timestamp file %s\n", file->source_name);
+            }
+            CloseHandle(open_file);
+#else
+            if (stat(file->target_name, &stbuf) == -1)
+            {
+                error_exit("Can't stat timestamp file %s\n", file->source_name);
+            }
+            convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
+#endif
+        }
+        else
+        {
+            if (verbosity == VERBOSE)
+            {
+                printf("%d: file %s (from %s)\n",
+                       d->level,
+                       file->target_name,
+                       file->source_name);
+            }
+            new_d = new_empty_dirrecord(d, FALSE);
+            parse_filename_into_dirrecord(file->target_name, new_d, FALSE);
+#if _WIN32
+            if ((open_file = CreateFileA(file->source_name,
+                                         GENERIC_READ,
+                                         FILE_SHARE_READ,
+                                         NULL,
+                                         OPEN_EXISTING,
+                                         FILE_ATTRIBUTE_NORMAL,
+                                         NULL)) == INVALID_HANDLE_VALUE)
+            {
+                error_exit("Can't open file %s\n", file->source_name);
+            }
+            if (get_cd_file_time(open_file, &new_d->date_and_time) == -1)
+            {
+                error_exit("Can't stat file %s\n", file->source_name);
+            }
+            if (!GetFileSizeEx(open_file, &file_size))
+            {
+                error_exit("Can't get file size of %s\n", file->source_name);
+            }
+            new_d->size = new_d->joliet_size = file_size.QuadPart;
+            new_d->orig_name = file->source_name;
+            CloseHandle(open_file);
+#else
+            if (stat(file->source_name, &stbuf) == -1)
+            {
+                error_exit("Can't find '%s' (target %s)\n",
+                           file->source_name,
+                           file->target_name);
+            }
+            convert_date_and_time(&new_d->date_and_time, &stbuf.st_mtime);
+            new_d->size = new_d->joliet_size = stbuf.st_size;
+            new_d->orig_name = file->source_name;
+#endif
+        }
+    }
+
+    for (child = dir->child; child; child = child->next)
+    {
+        if (verbosity == VERBOSE)
+        {
+            printf("%d: directory %s\n", d->level, child->case_name);
+        }
+        new_d = new_empty_dirrecord(d, TRUE);
+        parse_filename_into_dirrecord(child->case_name, new_d, TRUE);
+        scan_specified_files(new_d, child);
+    }
+
+    /* sort directory */
+    d->first_record = sort_linked_list(d->first_record,
+                                       0,
+                                       compare_directory_order);
+    source[0] = 0;
+    end_source = source;
+}
+
 /*-----------------------------------------------------------------------------
 This function loads the file specifications for the file or directory
 corresponding to the specified directory record into the source[] buffer. It
@@ -1455,14 +1607,23 @@ static void pass(void)
           }
           else
           {
+            const char *file_source;
             old_end_source = end_source;
-            get_file_specifications(q);
-            *end_source = 0;
+            if (!q->orig_name)
+            {
+              get_file_specifications(q);
+              *end_source = 0;
+              file_source = source;
+            }
+            else
+            {
+              file_source = q->orig_name;
+            }
             if (verbosity == VERBOSE)
-              printf("Writing %s\n", source);
-            file = fopen(source, "rb");
+              printf("Writing contents of %s\n", file_source);
+            file = fopen(file_source, "rb");
             if (file == NULL)
-              error_exit("Can't open %s\n", source);
+              error_exit("Can't open %s\n", file_source);
             fseek(file, 0, SEEK_SET);
             while (size > 0)
             {
@@ -1472,7 +1633,7 @@ static void pass(void)
               if (fread (cd.buffer + cd.count, n, 1, file) < 1)
               {
                 fclose(file);
-                error_exit("Read error in file %s\n", source);
+                error_exit("Read error in file %s\n", file_source);
               }
               cd.count += n;
               if (cd.count == BUFFER_SIZE)
@@ -1626,23 +1787,71 @@ int main(int argc, char **argv)
   if (cd.filespecs[0] == 0)
     error_exit("Missing image file specifications");
 
+  if (source[0] != '@')
+  {
+    /* set source[] and end_source to source directory,
+     * with a terminating directory separator */
+    end_source = source + strlen(source);
+    if (end_source[-1] == ':')
+      *end_source++ = '.';
+    if (end_source[-1] != DIR_SEPARATOR_CHAR)
+      *end_source++ = DIR_SEPARATOR_CHAR;
 
-  // set source[] and end_source to source directory, with a terminating directory separator
-
-  end_source = source + strlen(source);
-  if (end_source[-1] == ':')
-    *end_source++ = '.';
-  if (end_source[-1] != DIR_SEPARATOR_CHAR)
-    *end_source++ = DIR_SEPARATOR_CHAR;
-
-  // scan all files and create directory structure in memory
+    /* scan all files and create directory structure in memory */
+    make_directory_records(&root);
+  }
+  else
+  {
+    char *trimmedline, *targetname, *srcname, *eq;
+    char lineread[1024];
+    FILE *f = fopen(source+1, "r");
+    if (!f)
+    {
+      error_exit("Can't open cd description %s\n", source+1);
+    }
+    while (fgets(lineread, sizeof(lineread), f))
+    {
+      /* We treat these characters as line endings */
+      trimmedline = strtok(lineread, "\t\r\n;");
+      eq = strchr(trimmedline, '=');
+      if (!eq)
+      {
+        char *normdir;
+        /* Treat this as a directory name */
+        targetname = trimmedline;
+        normdir = strdup(targetname);
+        normalize_dirname(normdir);
+        dir_hash_create_dir(&specified_files, targetname, normdir);
+        free(normdir);
+      }
+      else
+      {
+        targetname = strtok(lineread, "=");
+        srcname = strtok(NULL, "");
 
-  make_directory_records(&root);
+#if _WIN32
+        if (_access(srcname, R_OK) == 0)
+          dir_hash_add_file(&specified_files, srcname, targetname);
+        else
+          error_exit("can't access file '%s' (target %s)\n", srcname, targetname);
+#else
+        if (access(srcname, R_OK) == 0)
+          dir_hash_add_file(&specified_files, srcname, targetname);
+        else
+          error_exit("can't access file '%s' (target %s)\n", srcname, targetname);
+#endif
+      }
+    }
+    fclose(f);
 
-  // sort path table entries
+    /* scan all files and create directory structure in memory */
+    scan_specified_files(&root, &specified_files.root);
+  }
 
-  root.next_in_path_table = sort_linked_list(root.next_in_path_table, 1,
-    compare_path_table_order);
+  /* sort path table entries */
+  root.next_in_path_table = sort_linked_list(root.next_in_path_table,
+                                             1,
+                                             compare_path_table_order);
 
   // initialize CD-ROM write buffer
 
@@ -1704,9 +1913,9 @@ int main(int argc, char **argv)
   if (verbosity >= NORMAL)
     puts("CD-ROM image made successfully");
 
+  dir_hash_destroy(&specified_files);
   release_memory();
   return 0;
 }
 
-
 /* EOF */
diff --git a/reactos/tools/cdmake/dirhash.c b/reactos/tools/cdmake/dirhash.c
new file mode 100644 (file)
index 0000000..528b104
--- /dev/null
@@ -0,0 +1,205 @@
+#include <string.h>
+#include <malloc.h>
+#include "dirsep.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)
+    {
+        free(*dirname);
+        *dirname = malloc(1);
+        **dirname = 0;
+    }
+    else
+    {
+        char *newdata = malloc(last_slash - name + 1);
+        memcpy(newdata, name, last_slash - name);
+        newdata[last_slash - name] = 0;
+        free(*dirname);
+        *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;
+    return de;
+}
+
+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;
+}
+
+struct target_dir_entry *
+dir_hash_create_dir(struct target_dir_hash *dh, const char *casename, const char *targetnorm)
+{
+    unsigned int hashcode;
+    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("");
+        hashcode = djb_hash("");
+        dh->buckets[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);
+    hashcode = djb_hash(targetnorm);
+    de = malloc(sizeof(*de));
+    memset(de, 0, sizeof(*de));
+    de->parent = parent_de;
+    de->normalized_name = strdup(targetnorm);
+    de->case_name = strdup(chop_filename(casename));
+    de->next = parent_de->child;
+    parent_de->child = de;
+    ent = &dh->buckets[hashcode % NUM_DIR_HASH_BUCKETS];
+    while ((*ent))
+    {
+        ent = &(*ent)->next;
+    }
+    *ent = de;
+    return de;
+}
+
+void dir_hash_add_file(struct target_dir_hash *dh, const char *source, const char *target)
+{
+    unsigned int hashcode;
+    struct target_file *tf;
+    struct target_dir_entry *de;
+    const char *filename = chop_filename(target);
+    char *targetdir = NULL;
+    char *targetnorm;
+    chop_dirname(target, &targetdir);
+    targetnorm = strdup(targetdir);
+    normalize_dirname(targetnorm);
+    de = dir_hash_create_dir(dh, targetdir, targetnorm);
+    tf = malloc(sizeof(*tf));
+    memset(tf, 0, sizeof(*tf));
+    tf->next = de->head;
+    de->head = tf;
+    tf->source_name = strdup(source);
+    tf->target_name = strdup(filename);
+}
+
+struct target_dir_entry *
+dir_hash_next_dir(struct target_dir_hash *dh, struct target_dir_traversal *t) 
+{
+    if (t->i == -1) 
+        return NULL;
+    if (!t->it)
+    {
+        while (++t->i != NUM_DIR_HASH_BUCKETS)
+        {
+            if (dh->buckets[t->i])
+            {
+                t->it = dh->buckets[t->i];
+                return t->it;
+            }
+        }
+        t->i = -1;
+        return NULL;
+    }
+    else
+    {
+        t->it = t->it->next;
+        if (!t->it)
+        {
+            t->i = -1;
+            return NULL;
+        }
+        else
+            return t->it;
+    }
+}
+
+void dir_hash_destroy_dir(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(te);
+        free(te);
+    }
+    while ((tf = de->head))
+    {
+        de->head = tf->next;
+        free(tf->source_name);
+        free(tf->target_name);
+        free(tf);
+    }
+    free(de->normalized_name);
+    free(de->case_name);
+}
+
+void dir_hash_destroy(struct target_dir_hash *dh)
+{
+    dir_hash_destroy_dir(&dh->root);
+}
diff --git a/reactos/tools/cdmake/dirhash.h b/reactos/tools/cdmake/dirhash.h
new file mode 100644 (file)
index 0000000..874f631
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _REACTOS_TOOLS_CDMAKE_DIRHASH_H_
+#define _REACTOS_TOOLS_CDMAKE_DIRHASH_H_
+
+#define NUM_DIR_HASH_BUCKETS 1024
+
+struct target_file {
+    struct target_file *next;
+    char *source_name;
+    char *target_name;
+};
+
+struct target_dir_entry {
+    struct target_dir_entry *next;
+    struct target_dir_entry *parent;
+    struct target_dir_entry *child;
+    struct target_file *head;
+    char *normalized_name;
+    char *case_name;
+};
+
+struct target_dir_hash {
+    struct target_dir_entry *buckets[NUM_DIR_HASH_BUCKETS];
+    struct target_dir_entry root;
+};
+
+struct target_dir_traversal {
+    struct target_dir_entry *it;
+    int i;
+};
+
+void normalize_dirname(char *filename);
+void dir_hash_add_file(struct target_dir_hash *dh, const char *source, const char *target);
+struct target_dir_entry *
+dir_hash_create_dir(struct target_dir_hash *dh, const char *casename, const char *targetnorm);
+struct target_dir_entry *dir_hash_next_dir(struct target_dir_hash *dh, struct target_dir_traversal *t);
+void dir_hash_destroy(struct target_dir_hash *dh);
+
+#endif//_REACTOS_TOOLS_CDMAKE_DIRHASH_H_
diff --git a/reactos/tools/cdmake/dirsep.h b/reactos/tools/cdmake/dirsep.h
new file mode 100644 (file)
index 0000000..c456c95
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _WIN32
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+#define DIR_SEPARATOR_CHAR '/'
+#define DIR_SEPARATOR_STRING "/"
+#else
+#define DIR_SEPARATOR_CHAR '\\'
+#define DIR_SEPARATOR_STRING "\\"
+#endif