2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS CD-ROM Maker
4 * FILE: tools/cdmake/dirhash.c
5 * PURPOSE: CD-ROM Premastering Utility - Directory names hashing
6 * PROGRAMMERS: Art Yerkes
16 #define max(a, b) ((a) > (b) ? (a) : (b))
19 /* This is the famous DJB hash */
21 djb_hash(const char *name
)
23 unsigned int val
= 5381;
26 for (i
= 0; name
[i
]; i
++)
28 val
= (33 * val
) + name
[i
];
35 split_path(const char *path
, char **dirname
, char **filename
/* OPTIONAL */)
39 /* Retrieve the file name */
40 char *last_slash_1
= strrchr(path
, '/');
41 char *last_slash_2
= strrchr(path
, '\\');
43 if (last_slash_1
|| last_slash_2
)
44 result
= max(last_slash_1
, last_slash_2
) + 1;
48 /* Duplicate the file name for the user if needed */
50 *filename
= strdup(result
);
52 /* Remove any trailing directory separators */
53 while (result
> path
&& (*(result
-1) == '/' || *(result
-1) == '\\'))
56 /* Retrieve and duplicate the directory */
57 *dirname
= malloc(result
- path
+ 1);
59 memcpy(*dirname
, path
, result
- path
);
60 (*dirname
)[result
- path
] = '\0'; // NULL-terminate
63 void normalize_dirname(char *filename
)
68 for (i
= 0, tgt
= 0; filename
[i
]; i
++)
72 if (filename
[i
] != '/' && filename
[i
] != '\\')
74 filename
[tgt
++] = toupper(filename
[i
]);
80 if (filename
[i
] == '/' || filename
[i
] == '\\')
83 filename
[tgt
++] = DIR_SEPARATOR_CHAR
;
87 filename
[tgt
++] = toupper(filename
[i
]);
91 filename
[tgt
] = '\0'; // NULL-terminate
94 static struct target_dir_entry
*
95 get_entry_by_normname(struct target_dir_hash
*dh
, const char *norm
)
97 unsigned int hashcode
;
98 struct target_dir_entry
*de
;
99 hashcode
= djb_hash(norm
);
100 de
= dh
->buckets
[hashcode
% NUM_DIR_HASH_BUCKETS
];
101 while (de
&& strcmp(de
->normalized_name
, norm
))
102 de
= de
->next_dir_hash_entry
;
107 delete_entry(struct target_dir_hash
*dh
, struct target_dir_entry
*de
)
109 struct target_dir_entry
**ent
;
110 ent
= &dh
->buckets
[de
->hashcode
% NUM_DIR_HASH_BUCKETS
];
111 while (*ent
&& ((*ent
) != de
))
112 ent
= &(*ent
)->next_dir_hash_entry
;
114 *ent
= (*ent
)->next_dir_hash_entry
;
117 struct target_dir_entry
*
118 dir_hash_create_dir(struct target_dir_hash
*dh
, const char *casename
, const char *targetnorm
)
120 struct target_dir_entry
*de
, *parent_de
;
121 char *parentcase
= NULL
;
122 char *case_name
= NULL
;
123 char *parentname
= NULL
;
124 struct target_dir_entry
**ent
;
126 if (!dh
->root
.normalized_name
)
128 dh
->root
.normalized_name
= strdup("");
129 dh
->root
.case_name
= strdup("");
130 dh
->root
.hashcode
= djb_hash("");
131 dh
->buckets
[dh
->root
.hashcode
% NUM_DIR_HASH_BUCKETS
] = &dh
->root
;
134 /* Check whether the directory was already created and just return it if so */
135 de
= get_entry_by_normname(dh
, targetnorm
);
140 * If *case_name == '\0' after the following call to split_path(...),
141 * for example in the case where casename == "subdir/dir/", then just
142 * create the directories "subdir" and "dir" by a recursive call to
143 * dir_hash_create_dir(...) and return 'parent_de' instead (see after).
144 * We do not (and we never) create a no-name directory inside it.
146 split_path(casename
, &parentcase
, &case_name
);
147 split_path(targetnorm
, &parentname
, NULL
);
148 parent_de
= dir_hash_create_dir(dh
, parentcase
, parentname
);
152 /* See the remark above */
159 /* Now create the directory */
160 de
= calloc(1, sizeof(*de
));
161 de
->parent
= parent_de
;
162 de
->normalized_name
= strdup(targetnorm
);
163 de
->case_name
= case_name
;
164 de
->hashcode
= djb_hash(targetnorm
);
166 de
->next
= parent_de
->child
;
167 parent_de
->child
= de
;
169 ent
= &dh
->buckets
[de
->hashcode
% NUM_DIR_HASH_BUCKETS
];
171 ent
= &(*ent
)->next_dir_hash_entry
;
178 dir_hash_add_file(struct target_dir_hash
*dh
, const char *source
, const char *target
)
180 struct target_file
*tf
;
181 struct target_dir_entry
*de
;
182 char *targetdir
= NULL
;
183 char *targetfile
= NULL
;
186 /* First create the directory; check whether the file name is valid and bail out if not */
187 split_path(target
, &targetdir
, &targetfile
);
194 targetnorm
= strdup(targetdir
);
195 normalize_dirname(targetnorm
);
196 de
= dir_hash_create_dir(dh
, targetdir
, targetnorm
);
200 /* Now add the file */
201 tf
= calloc(1, sizeof(*tf
));
204 tf
->source_name
= strdup(source
);
205 tf
->target_name
= targetfile
;
211 dir_hash_destroy_dir(struct target_dir_hash
*dh
, struct target_dir_entry
*de
)
213 struct target_file
*tf
;
214 struct target_dir_entry
*te
;
216 while ((te
= de
->child
))
218 de
->child
= te
->next
;
219 dir_hash_destroy_dir(dh
, te
);
222 while ((tf
= de
->head
))
225 free(tf
->source_name
);
226 free(tf
->target_name
);
230 delete_entry(dh
, de
);
231 free(de
->normalized_name
);
235 void dir_hash_destroy(struct target_dir_hash
*dh
)
237 dir_hash_destroy_dir(dh
, &dh
->root
);