1 /**
2  * @file libcomprex/directory.c Directory structures
3  *
4  * $Id: directory.c,v 1.39 2003/01/01 06:22:35 chipx86 Exp $
5  *
6  * @Copyright (C) 2001-2003 The GNUpdate Project.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA  02111-1307, USA.
22  */
23 #include <libcomprex/internal.h>
24 
25 static CxDirectory *
__getExistingPart(CxDirectory * base,const char * path,char ** invalidPart)26 __getExistingPart(CxDirectory *base, const char *path, char **invalidPart)
27 {
28 	CxDirectory *dir = NULL;
29 	char *newPath;
30 	const char *p, *p2;
31 
32 	if (base == NULL || path == NULL || *path == '\0')
33 		return NULL;
34 
35 	newPath = cxFixPath(path);
36 	p = newPath;
37 
38 	if (invalidPart != NULL)
39 		*invalidPart = NULL;
40 
41 	if (*p == '/')
42 	{
43 		/* Proceed to the root directory. */
44 		dir = cxGetArchiveRoot(cxGetDirArchive(base));
45 
46 		p++;
47 	}
48 	else
49 		dir = base;
50 
51 	for (; p != NULL && *p != '\0'; p = (p2 == NULL ? NULL : p2 + 1))
52 	{
53 		char *dirname;
54 		int len;
55 
56 		/* Find the start of the next dir, if any, and NUL-terminate it. */
57 		p2 = strchr(p, '/');
58 
59 		if (p2 != NULL)
60 			len = p2 - p;
61 		else
62 		{
63 			len = strlen(p);
64 		}
65 
66 		/* See if we're going down a level. */
67 		if (!strncmp(p, "..", 2) && (*(p + 2) == '/' || *(p + 2) == '\0'))
68 		{
69 			if (cxGetDirParent(dir) != NULL)
70 				dir = cxGetDirParent(dir);
71 
72 			continue;
73 		}
74 
75 		/* Get this path. */
76 		MEM_CHECK(dirname = (char *)malloc(len + 1));
77 
78 		strncpy(dirname, p, len);
79 		dirname[len] = '\0';
80 
81 		if (strncmp(dirname, ".", 2))
82 		{
83 			CxDirectory *tempDir;
84 
85 			/* Try to see if this exists. */
86 			for (tempDir = cxGetFirstSubDir(dir);
87 				 tempDir != NULL && strcmp(cxGetDirName(tempDir), dirname);
88 				 tempDir = cxGetNextDir(tempDir))
89 				;
90 
91 			if (tempDir == NULL)
92 			{
93 				free(dirname);
94 
95 				break;
96 			}
97 
98 			/*
99 			 * On the next iteration, we'll be working with this
100 			 * new-found directory.
101 			 */
102 			dir = tempDir;
103 		}
104 
105 		free(dirname);
106 	}
107 
108 	if (invalidPart != NULL && p != NULL && *p != '\0')
109 		*invalidPart = strdup(p);
110 
111 	free(newPath);
112 
113 	return dir;
114 }
115 
116 CxDirectory *
cxNewDirectory(void)117 cxNewDirectory(void)
118 {
119 	CxDirectory *dir;
120 
121 	dir = (CxDirectory *)cxNewFsNode();
122 
123 	MEM_CHECK(dir->u.dir = (CxDirectoryData *)malloc(sizeof(CxDirectoryData)));
124 	memset(dir->u.dir, 0, sizeof(CxDirectoryData));
125 
126 	cxSetFsNodeType(dir, CX_FSNODETYPE_DIRECTORY);
127 
128 	return dir;
129 }
130 
131 void
cxDestroyDirectory(CxDirectory * dir)132 cxDestroyDirectory(CxDirectory *dir)
133 {
134 	CxFile *curFile, *nextFile;
135 	CxDirectory *curDir, *nextDir;
136 
137 	if (dir == NULL || !CX_IS_DIRECTORY(dir) || dir->refCount == 0)
138 		return;
139 
140 	if ((dir->refCount - 1) > 0)
141 		return;
142 
143 	for (curFile = cxGetFirstFile(dir);
144 		 curFile != NULL;
145 		 curFile = nextFile)
146 	{
147 		nextFile = cxGetNextFile(curFile);
148 
149 		cxDestroyFile(curFile);
150 	}
151 
152 	for (curDir = cxGetFirstSubDir(dir);
153 		 curDir != NULL;
154 		 curDir = nextDir)
155 	{
156 		nextDir = cxGetNextDir(curDir);
157 
158 		cxDestroyDirectory(curDir);
159 	}
160 
161 	free(dir->u.dir);
162 
163 	cxDestroyFsNode(dir);
164 }
165 
166 void
cxSetDirArchive(CxDirectory * dir,CxArchive * archive)167 cxSetDirArchive(CxDirectory *dir, CxArchive *archive)
168 {
169 	cxSetFsNodeArchive(dir, archive);
170 }
171 
172 void
cxSetDirParent(CxDirectory * dir,CxDirectory * parent)173 cxSetDirParent(CxDirectory *dir, CxDirectory *parent)
174 {
175 	cxSetFsNodeParent(dir, parent);
176 }
177 
178 void
cxSetDirName(CxDirectory * dir,const char * name)179 cxSetDirName(CxDirectory *dir, const char *name)
180 {
181 	cxSetFsNodeName(dir, name);
182 }
183 
184 void
cxSetDirPhysicalPath(CxDirectory * dir,const char * path)185 cxSetDirPhysicalPath(CxDirectory *dir, const char *path)
186 {
187 	if (dir == NULL)
188 		return;
189 
190 	if (dir->u.dir->physPath != NULL)
191 		free(dir->u.dir->physPath);
192 
193 	dir->u.dir->physPath = (path == NULL ? NULL : strdup(path));
194 }
195 
196 void
cxSetDirMode(CxDirectory * dir,mode_t mode)197 cxSetDirMode(CxDirectory *dir, mode_t mode)
198 {
199 	cxSetFsNodeMode(dir, mode);
200 }
201 
202 void
cxSetDirUid(CxDirectory * dir,uid_t uid)203 cxSetDirUid(CxDirectory *dir, uid_t uid)
204 {
205 	cxSetFsNodeUid(dir, uid);
206 }
207 
208 void
cxSetDirGid(CxDirectory * dir,gid_t gid)209 cxSetDirGid(CxDirectory *dir, gid_t gid)
210 {
211 	cxSetFsNodeGid(dir, gid);
212 }
213 
214 void
cxSetDirDate(CxDirectory * dir,time_t date)215 cxSetDirDate(CxDirectory *dir, time_t date)
216 {
217 	cxSetFsNodeDate(dir, date);
218 }
219 
220 void
cxSetDirLocal(CxDirectory * dir,char isLocal)221 cxSetDirLocal(CxDirectory *dir, char isLocal)
222 {
223 	cxSetFsNodeLocal(dir, isLocal);
224 }
225 
226 
227 CxArchive *
cxGetDirArchive(CxDirectory * dir)228 cxGetDirArchive(CxDirectory *dir)
229 {
230 	return cxGetFsNodeArchive(dir);
231 }
232 
233 CxDirectory *
cxGetDirParent(CxDirectory * dir)234 cxGetDirParent(CxDirectory *dir)
235 {
236 	return cxGetFsNodeParent(dir);
237 }
238 
239 const char *
cxGetDirName(CxDirectory * dir)240 cxGetDirName(CxDirectory *dir)
241 {
242 	return cxGetFsNodeName(dir);
243 }
244 
245 const char *
cxGetDirPath(CxDirectory * dir)246 cxGetDirPath(CxDirectory *dir)
247 {
248 	return cxGetFsNodePath(dir);
249 }
250 
251 const char *
cxGetDirPhysicalPath(CxDirectory * dir)252 cxGetDirPhysicalPath(CxDirectory *dir)
253 {
254 	if (dir == NULL)
255 		return NULL;
256 
257 	return dir->u.dir->physPath;
258 }
259 
260 mode_t
cxGetDirMode(CxDirectory * dir)261 cxGetDirMode(CxDirectory *dir)
262 {
263 	return cxGetFsNodeMode(dir);
264 }
265 
266 uid_t
cxGetDirUid(CxDirectory * dir)267 cxGetDirUid(CxDirectory *dir)
268 {
269 	return cxGetFsNodeUid(dir);
270 }
271 
272 gid_t
cxGetDirGid(CxDirectory * dir)273 cxGetDirGid(CxDirectory *dir)
274 {
275 	return cxGetFsNodeGid(dir);
276 }
277 
278 time_t
cxGetDirDate(CxDirectory * dir)279 cxGetDirDate(CxDirectory *dir)
280 {
281 	return cxGetFsNodeDate(dir);
282 }
283 
284 char
cxIsDirLocal(CxDirectory * dir)285 cxIsDirLocal(CxDirectory *dir)
286 {
287 	return cxIsFsNodeLocal(dir);
288 }
289 
290 unsigned int
cxGetFileCount(CxDirectory * dir)291 cxGetFileCount(CxDirectory *dir)
292 {
293 	if (dir == NULL || !CX_IS_DIRECTORY(dir))
294 		return 0;
295 
296 	return dir->u.dir->fileCount;
297 }
298 
299 unsigned int
cxGetSubDirCount(CxDirectory * dir)300 cxGetSubDirCount(CxDirectory *dir)
301 {
302 	if (dir == NULL || !CX_IS_DIRECTORY(dir))
303 		return 0;
304 
305 	return dir->u.dir->subdirCount;
306 }
307 
308 CxDirectory *
cxGetDirectory(CxDirectory * base,const char * path)309 cxGetDirectory(CxDirectory *base, const char *path)
310 {
311 	CxDirectory *dir;
312 	char *remaining;
313 
314 	if (base == NULL || !CX_IS_DIRECTORY(base) ||
315 		path == NULL || *path == '\0')
316 	{
317 		return NULL;
318 	}
319 
320 	dir = __getExistingPart(base, path, &remaining);
321 
322 	/* Make sure the ENTIRE path exists! */
323 	if (remaining == NULL)
324 		return dir;
325 
326 	free(remaining);
327 
328 	return NULL;
329 }
330 
331 CxFile *
cxGetFile(CxDirectory * base,const char * path)332 cxGetFile(CxDirectory *base, const char *path)
333 {
334 	CxDirectory *dir  = NULL;
335 	CxFile      *file = NULL;
336 	char *temp;
337 
338 	if (base == NULL || !CX_IS_DIRECTORY(base) ||
339 		path == NULL || *path == '\0')
340 	{
341 		return NULL;
342 	}
343 
344 	temp = cxGetBasePath(path);
345 
346 	if (temp != NULL)
347 	{
348 		dir = cxGetDirectory(base, temp);
349 		free(temp);
350 	}
351 
352 	if (dir == NULL)
353 		dir = base;
354 
355 	/* Now we should have the directory. Get the filename. */
356 	temp = cxGetBaseName(path);
357 
358 	if (temp == NULL || *temp == '\0')
359 	{
360 		/*
361 		 * No base name. Oops, error on the user's part, most likely.
362 		 *
363 		 * Either an out of memory, or a trailing '/'
364 		 */
365 
366 		if (temp != NULL)
367 			free(temp);
368 
369 		return NULL;
370 	}
371 
372 	/* Find the file. */
373 	for (file = cxGetFirstFile(dir);
374 		 file != NULL && strcmp(cxGetFileName(file), temp);
375 		 file = cxGetNextFile(file))
376 		;
377 
378 	free(temp);
379 
380 	return file;
381 }
382 
383 CxDirectory *
cxMkDir(CxDirectory * base,const char * path)384 cxMkDir(CxDirectory *base, const char *path)
385 {
386 	CxDirectory *dir = NULL, *newDir;
387 	char *newPath;
388 	char *p, *p2;
389 
390 	if (base == NULL || !CX_IS_DIRECTORY(base) ||
391 		path == NULL || *path == '\0')
392 	{
393 		return NULL;
394 	}
395 
396 	dir = __getExistingPart(base, path, &newPath);
397 
398 	if (newPath == NULL)
399 		return dir; /* XXX NULL? This means the directory already exists! */
400 
401 	for (p = newPath;
402 		 p != NULL && *p != '\0';
403 		 p = (p2 == NULL ? NULL : p2 + 1))
404 	{
405 		/* Find the start of the next dir, if any, and NUL-terminate it. */
406 		p2 = strchr(p, '/');
407 
408 		if (p2 != NULL)
409 			*p2 = '\0';
410 
411 		/* Create the directory and add it. */
412 		newDir = cxNewDirectory();
413 		cxSetDirName(newDir, p);
414 
415 		cxDirAddSubDir(dir, newDir);
416 
417 		dir = newDir;
418 	}
419 
420 	free(newPath);
421 
422 	return dir;
423 }
424 
425 static void
__dirAddChild(CxDirectory * dir,CxFsNode * node)426 __dirAddChild(CxDirectory *dir, CxFsNode *node)
427 {
428 	if (dir == NULL || node == NULL)
429 		return;
430 
431 	if (dir->u.dir->children == NULL)
432 		dir->u.dir->children = node;
433 
434 	node->prev = dir->u.dir->lastChild;
435 
436 	if (dir->u.dir->lastChild != NULL)
437 		dir->u.dir->lastChild->next = node;
438 
439 	dir->u.dir->lastChild = node;
440 
441 	cxSetFsNodeArchive(node, cxGetDirArchive(dir));
442 	cxSetFsNodeParent(node,  dir);
443 }
444 
445 void
cxDirAddFile(CxDirectory * dir,CxFile * file)446 cxDirAddFile(CxDirectory *dir, CxFile *file)
447 {
448 	CxArchive *archive;
449 
450 	if (dir == NULL || !CX_IS_DIRECTORY(dir) || file == NULL)
451 		return;
452 
453 	__dirAddChild(dir, file);
454 
455 	archive = cxGetDirArchive(dir);
456 
457 	dir->u.dir->fileCount++;
458 	archive->fileCount++;
459 
460 	cxSetArchiveSize(archive, cxGetArchiveSize(archive) + cxGetFileSize(file));
461 }
462 
463 void
cxDirAddSubDir(CxDirectory * dir,CxDirectory * subdir)464 cxDirAddSubDir(CxDirectory *dir, CxDirectory *subdir)
465 {
466 	if (dir == NULL || subdir == NULL ||
467 		!CX_IS_DIRECTORY(dir) || !CX_IS_DIRECTORY(subdir))
468 	{
469 		return;
470 	}
471 
472 	__dirAddChild(dir, subdir);
473 
474 	dir->u.dir->subdirCount++;
475 }
476 
477 static void
__dirRemoveChild(CxDirectory * dir,CxFsNode * node)478 __dirRemoveChild(CxDirectory *dir, CxFsNode *node)
479 {
480 	if (dir == NULL || node == NULL || cxGetFsNodeParent(node) != dir)
481 		return;
482 
483 	if (node->prev == NULL)
484 		dir->u.dir->children = node->next;
485 	else
486 		node->prev->next = node->next;
487 
488 	if (node->next == NULL)
489 		dir->u.dir->lastChild = node->prev;
490 	else
491 		node->next->prev = node->prev;
492 
493 	cxSetFsNodeArchive(node, NULL);
494 
495 	cxDestroyFsNode(node);
496 }
497 
498 void
cxDirRemoveFile(CxDirectory * dir,CxFile * file)499 cxDirRemoveFile(CxDirectory *dir, CxFile *file)
500 {
501 	CxArchive *archive;
502 
503 	if (dir == NULL || file == NULL || !CX_IS_DIRECTORY(dir))
504 		return;
505 
506 	__dirRemoveChild(dir, file);
507 
508 	archive = cxGetDirArchive(dir);
509 
510 	dir->u.dir->fileCount--;
511 	archive->fileCount--;
512 
513 	cxSetArchiveSize(archive, cxGetArchiveSize(archive) - cxGetFileSize(file));
514 }
515 
516 void
cxDirRemoveSubDir(CxDirectory * dir,CxDirectory * subdir)517 cxDirRemoveSubDir(CxDirectory *dir, CxDirectory *subdir)
518 {
519 	if (dir == NULL || subdir == NULL ||
520 		!CX_IS_DIRECTORY(dir) || !CX_IS_DIRECTORY(subdir))
521 		return;
522 
523 	__dirRemoveChild(dir, subdir);
524 
525 	dir->u.dir->subdirCount--;
526 }
527 
528 CxFile *
cxGetFirstFile(CxDirectory * dir)529 cxGetFirstFile(CxDirectory *dir)
530 {
531 	CxFile *file;
532 
533 	if (dir == NULL || !CX_IS_DIRECTORY(dir))
534 		return NULL;
535 
536 	for (file = dir->u.dir->children;
537 		 file != NULL && cxGetFsNodeType(file) != CX_FSNODETYPE_FILE;
538 		 file = file->next)
539 		;
540 
541 	return file;
542 }
543 
544 CxDirectory *
cxGetFirstSubDir(CxDirectory * dir)545 cxGetFirstSubDir(CxDirectory *dir)
546 {
547 	CxDirectory *subdir;
548 
549 	if (dir == NULL || !CX_IS_DIRECTORY(dir))
550 		return NULL;
551 
552 	for (subdir = dir->u.dir->children;
553 		 subdir != NULL && !CX_IS_DIRECTORY(subdir);
554 		 subdir = subdir->next)
555 		;
556 
557 	return subdir;
558 }
559 
560 CxDirectory *
cxGetPreviousDir(CxDirectory * dir)561 cxGetPreviousDir(CxDirectory *dir)
562 {
563 	if (dir == NULL || !CX_IS_DIRECTORY(dir))
564 		return NULL;
565 
566 	for (dir = dir->prev;
567 		 dir != NULL && !CX_IS_DIRECTORY(dir);
568 		 dir = dir->prev)
569 		;
570 
571 	return dir;
572 }
573 
574 CxDirectory *
cxGetNextDir(CxDirectory * dir)575 cxGetNextDir(CxDirectory *dir)
576 {
577 	if (dir == NULL || !CX_IS_DIRECTORY(dir))
578 		return NULL;
579 
580 	for (dir = dir->next;
581 		 dir != NULL && !CX_IS_DIRECTORY(dir);
582 		 dir = dir->next)
583 		;
584 
585 	return dir;
586 }
587 
588