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