From fe0958fb253fd116da33242fdc6af005d8d13e74 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Sun, 21 Jul 2013 13:33:03 +0000 Subject: [PATCH] * Improve the way we create bootcd, livecd and bootcdregtest. The effort results in ~28% *smaller* build folder, and *much* faster generation of the ISOs. [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 | 3 + reactos/boot/CMakeLists.txt | 39 +--- reactos/cmake/CMakeMacros.cmake | 56 ++++-- reactos/media/vgafonts/CMakeLists.txt | 1 - reactos/tools/cdmake/CMakeLists.txt | 2 +- reactos/tools/cdmake/cdmake.c | 275 ++++++++++++++++++++++---- reactos/tools/cdmake/dirhash.c | 205 +++++++++++++++++++ reactos/tools/cdmake/dirhash.h | 38 ++++ reactos/tools/cdmake/dirsep.h | 10 + 9 files changed, 545 insertions(+), 84 deletions(-) create mode 100644 reactos/tools/cdmake/dirhash.c create mode 100644 reactos/tools/cdmake/dirhash.h create mode 100644 reactos/tools/cdmake/dirsep.h diff --git a/reactos/CMakeLists.txt b/reactos/CMakeLists.txt index 201c4e3dd58..d0c83d73e4d 100644 --- a/reactos/CMakeLists.txt +++ b/reactos/CMakeLists.txt @@ -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() diff --git a/reactos/boot/CMakeLists.txt b/reactos/boot/CMakeLists.txt index 53aacd87f5f..c9341f49419 100644 --- a/reactos/boot/CMakeLists.txt +++ b/reactos/boot/CMakeLists.txt @@ -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) diff --git a/reactos/cmake/CMakeMacros.cmake b/reactos/cmake/CMakeMacros.cmake index 6d58334da36..187ed769c43 100644 --- a/reactos/cmake/CMakeMacros.cmake +++ b/reactos/cmake/CMakeMacros.cmake @@ -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}) diff --git a/reactos/media/vgafonts/CMakeLists.txt b/reactos/media/vgafonts/CMakeLists.txt index a9232b22c0b..2a00f5c75d5 100644 --- a/reactos/media/vgafonts/CMakeLists.txt +++ b/reactos/media/vgafonts/CMakeLists.txt @@ -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) diff --git a/reactos/tools/cdmake/CMakeLists.txt b/reactos/tools/cdmake/CMakeLists.txt index bb3669b5d64..d8fb74cb855 100644 --- a/reactos/tools/cdmake/CMakeLists.txt +++ b/reactos/tools/cdmake/CMakeLists.txt @@ -1,2 +1,2 @@ +add_executable(cdmake cdmake.c dirhash.c llmosrt.c) -add_executable(cdmake cdmake.c llmosrt.c) diff --git a/reactos/tools/cdmake/cdmake.c b/reactos/tools/cdmake/cdmake.c index fbe1a5bfac0..e82e7c987c7 100644 --- a/reactos/tools/cdmake/cdmake.c +++ b/reactos/tools/cdmake/cdmake.c @@ -33,8 +33,13 @@ #include #include #ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include # include # include +# ifdef _MSC_VER +# define R_OK 4 +# endif #else # if defined(__FreeBSD__) || defined(__APPLE__) # include @@ -45,29 +50,20 @@ # include # include # include +# define TRUE 1 +# define FALSE 0 #endif // _WIN32 #include #include #include -#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 + /* 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, ""); - end_source = source + strlen(source); - if (end_source[-1] == ':') - *end_source++ = '.'; - if (end_source[-1] != DIR_SEPARATOR_CHAR) - *end_source++ = DIR_SEPARATOR_CHAR; +#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); - // scan all files and create directory structure in memory + /* scan all files and create directory structure in memory */ + scan_specified_files(&root, &specified_files.root); + } - make_directory_records(&root); - - // sort path table entries - - 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 index 00000000000..528b10496ea --- /dev/null +++ b/reactos/tools/cdmake/dirhash.c @@ -0,0 +1,205 @@ +#include +#include +#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 index 00000000000..874f6315ccc --- /dev/null +++ b/reactos/tools/cdmake/dirhash.h @@ -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 index 00000000000..c456c958abe --- /dev/null +++ b/reactos/tools/cdmake/dirsep.h @@ -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