2 * libdpkg - Debian packaging suite library routines
3 * treewalk.c - directory tree walk support
5 * Copyright © 2013-2016 Guillem Jover <guillem@debian.org>
7 * This is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
25 #include <sys/types.h>
32 #include <dpkg/dpkg.h>
33 #include <dpkg/i18n.h>
34 #include <dpkg/treewalk.h>
36 /* treenode functions. */
38 typedef int treewalk_stat_func(const char *pathname
, struct stat
*st
);
41 struct treenode
*up
; /* Parent dir. */
42 struct treenode
*next
; /* Next node in dir. */
43 struct treenode
**down
; /* Dir contents. */
45 char *pathname
; /* Node pathname. */
46 char *virtname
; /* Node virtname. */
47 char *name
; /* Node name. */
49 struct stat
*stat
; /* Node metadata. */
52 size_t pathname_len
; /* Node pathname length. */
54 size_t down_used
; /* Number of used nodes in dir. */
55 size_t down_size
; /* Number of allocated nodes in dir. */
58 static inline struct treenode
*
61 return m_calloc(1, sizeof(struct treenode
));
64 static struct treenode
*
65 treenode_root_new(const char *rootdir
)
67 struct treenode
*node
;
69 node
= treenode_alloc();
71 node
->pathname
= m_strdup(rootdir
);
72 node
->pathname_len
= strlen(node
->pathname
);
73 node
->virtname
= node
->pathname
+ node
->pathname_len
;
74 node
->name
= strrchr(node
->pathname
, '/');
75 if (node
->name
== NULL
)
76 node
->name
= node
->pathname
;
83 static struct treenode
*
84 treenode_node_new(struct treenode
*root
, struct treenode
*dir
, const char *name
)
86 struct treenode
*node
;
88 node
= treenode_alloc();
91 node
->pathname
= str_fmt("%s/%s", node
->up
->pathname
, name
);
92 node
->pathname_len
= strlen(node
->pathname
);
93 node
->virtname
= node
->pathname
+ root
->pathname_len
+ 1;
94 node
->name
= node
->pathname
+ node
->up
->pathname_len
+ 1;
100 treenode_stat(struct treenode
*node
, treewalk_stat_func
*stat_func
)
105 node
->stat
= m_malloc(sizeof(*node
->stat
));
107 if (stat_func(node
->pathname
, node
->stat
) < 0)
108 ohshite(_("cannot stat pathname '%s'"), node
->pathname
);
110 node
->mode
= node
->stat
->st_mode
;
114 dirent_to_mode_type(struct dirent
*e
)
116 #ifdef _DIRENT_HAVE_D_TYPE
142 treenode_stat_shallow(struct treenode
*node
, struct dirent
*e
,
143 treewalk_stat_func
*stat_func
)
147 mode
= dirent_to_mode_type(e
);
148 if (mode
== 0 || S_ISDIR(mode
) || S_ISLNK(mode
))
149 treenode_stat(node
, stat_func
);
155 treenode_get_name(struct treenode
*node
)
161 treenode_get_pathname(struct treenode
*node
)
163 return node
->pathname
;
167 treenode_get_virtname(struct treenode
*node
)
169 return node
->virtname
;
173 treenode_get_mode(struct treenode
*node
)
179 treenode_get_stat(struct treenode
*node
)
181 treenode_stat(node
, lstat
);
186 treenode_get_parent(struct treenode
*node
)
192 treenode_is_dir(struct treenode
*node
)
194 return node
&& S_ISDIR(node
->mode
);
198 treenode_resize_down(struct treenode
*node
)
203 node
->down_size
*= 2;
204 else if (node
->stat
->st_nlink
> 4)
205 node
->down_size
= node
->stat
->st_nlink
* 2;
209 new_size
= node
->down_size
* sizeof(*node
);
210 node
->down
= m_realloc(node
->down
, new_size
);
214 treenode_cmp(const void *a
, const void *b
)
216 return strcmp((*(const struct treenode
**)a
)->name
,
217 (*(const struct treenode
**)b
)->name
);
221 treenode_sort_down(struct treenode
*dir
)
225 qsort(dir
->down
, dir
->down_used
, sizeof(struct treenode
*), treenode_cmp
);
227 /* Relink the nodes. */
228 for (i
= 0; i
< dir
->down_used
- 1; i
++)
229 dir
->down
[i
]->next
= dir
->down
[i
+ 1];
230 dir
->down
[i
]->next
= NULL
;
234 treenode_fill_down(struct treenode
*root
, struct treenode
*dir
,
235 enum treewalk_options options
)
239 treewalk_stat_func
*stat_func
;
241 if (options
& TREEWALK_FOLLOW_LINKS
)
246 d
= opendir(dir
->pathname
);
248 ohshite(_("cannot open directory '%s'"), dir
->pathname
);
250 while ((e
= readdir(d
)) != NULL
) {
251 struct treenode
*node
;
253 if (strcmp(e
->d_name
, ".") == 0 ||
254 strcmp(e
->d_name
, "..") == 0)
257 if (dir
->down_used
>= dir
->down_size
)
258 treenode_resize_down(dir
);
260 node
= treenode_node_new(root
, dir
, e
->d_name
);
261 if (options
& TREEWALK_FORCE_STAT
)
262 treenode_stat(node
, stat_func
);
264 treenode_stat_shallow(node
, e
, stat_func
);
266 dir
->down
[dir
->down_used
] = node
;
274 treenode_free_node(struct treenode
*node
)
276 free(node
->pathname
);
282 treenode_free_down(struct treenode
*node
)
286 if (!node
->down_size
)
289 for (i
= 0; i
< node
->down_used
; i
++)
290 treenode_free_node(node
->down
[i
]);
295 /* treeroot functions. */
298 struct treenode
*rootnode
;
300 struct treenode
*curdir
;
301 struct treenode
*curnode
;
303 enum treewalk_options options
;
304 struct treewalk_funcs func
;
308 treeroot_set_curdir(struct treeroot
*tree
, struct treenode
*node
)
314 treeroot_set_curnode(struct treeroot
*tree
, struct treenode
*node
)
316 tree
->curnode
= node
;
320 treeroot_skip_node(struct treeroot
*tree
, struct treenode
*node
)
323 return tree
->func
.skip(node
);
329 treeroot_fill_node(struct treeroot
*tree
, struct treenode
*dir
)
331 treenode_fill_down(tree
->rootnode
, dir
, tree
->options
);
335 treeroot_sort_node(struct treeroot
*tree
, struct treenode
*dir
)
337 static struct treenode
*down_empty
[] = { NULL
, NULL
};
339 if (dir
->down_used
== 0)
340 dir
->down
= down_empty
;
341 else if (tree
->func
.sort
)
342 tree
->func
.sort(dir
);
344 treenode_sort_down(dir
);
348 treeroot_visit_node(struct treeroot
*tree
, struct treenode
*node
)
350 if (tree
->func
.visit
== NULL
)
353 if (!treeroot_skip_node(tree
, node
))
354 tree
->func
.visit(node
);
358 * Open a new tree to be walked.
360 * @param rootdir The root directory to start walking the tree.
361 * @param options The options specifying how to walk the tree.
362 * @param func The functions callbacks.
365 treewalk_open(const char *rootdir
, enum treewalk_options options
,
366 const struct treewalk_funcs
*func
)
368 struct treeroot
*tree
;
369 struct treenode
*root
;
371 tree
= m_malloc(sizeof(*tree
));
373 tree
->options
= options
;
377 tree
->func
= TREEWALK_OBJECT
;
379 root
= treenode_root_new(rootdir
);
380 treenode_stat(root
, lstat
);
382 if (!treenode_is_dir(root
))
383 ohshit(_("treewalk root %s is not a directory"), rootdir
);
385 treeroot_set_curnode(tree
, root
);
386 tree
->rootnode
= tree
->curdir
= root
;
392 * Return the current node.
394 * This function is only needed if you want to start walking the tree from
395 * the root node. With something like:
398 * struct treeroot *tree;
399 * struct treenode *node;
401 * tree = treewalk_open(...);
402 * for (node = treewalk_node(tree); node; node = treewalk_next(tree))
404 * treewalk_close(tree);
407 * @param tree The tree structure.
410 treewalk_node(struct treeroot
*tree
)
412 return tree
->curnode
;
416 * Return the next node.
418 * This function works basically as an iterator. And will return NULL when
419 * the whole tree has been traversed. When starting it will skip the root
420 * node, so you might want to use treewalk_node() to get that, otherwise
421 * you could use it like this:
424 * struct treeroot *tree;
425 * struct treenode *node;
427 * tree = treewalk_open(...);
428 * while ((node = treewalk_next(tree))
430 * treewalk_close(tree);
433 * @param tree The tree structure.
436 treewalk_next(struct treeroot
*tree
)
438 struct treenode
*node
;
440 /* Handle rootless trees, such as uninitialized or fully traversed. */
441 if (tree
->rootnode
== NULL
)
444 node
= tree
->curnode
;
446 /* Handle end of tree. */
450 /* Get next node, descending or sidewide. */
451 if (treenode_is_dir(node
) && !treeroot_skip_node(tree
, node
)) {
452 struct treenode
*dir
;
454 treeroot_fill_node(tree
, node
);
455 treeroot_sort_node(tree
, node
);
456 treeroot_set_curdir(tree
, node
);
458 /* Check for directory loops. */
459 for (dir
= node
->up
; dir
; dir
= dir
->up
) {
460 if (dir
->stat
->st_dev
== node
->stat
->st_dev
&&
461 dir
->stat
->st_ino
== node
->stat
->st_ino
)
465 /* Skip directory loops. */
469 node
= node
->down
[0];
474 /* Back track node, ascending. */
475 while (node
== NULL
) {
476 struct treenode
*olddir
= tree
->curdir
;
478 if (tree
->curdir
->next
) {
479 /* Next entry in the parent directory. */
480 node
= tree
->curdir
->next
;
481 treeroot_set_curdir(tree
, olddir
->up
);
482 treenode_free_down(olddir
);
483 } else if (tree
->curdir
->up
) {
484 /* Next entry in the grand-parent directory. */
485 node
= tree
->curdir
->up
->next
;
486 treeroot_set_curdir(tree
, olddir
->up
->up
);
487 treenode_free_down(olddir
);
488 treenode_free_down(olddir
->up
);
490 /* Otherwise, we're in the rootnode. */
491 treenode_free_down(olddir
);
492 treenode_free_node(olddir
);
496 if (tree
->curdir
== NULL
) {
497 treenode_free_node(tree
->rootnode
);
498 tree
->rootnode
= NULL
;
503 treeroot_set_curnode(tree
, node
);
509 * Closes the tree being walked.
511 * It will free any resources previously allocated.
514 treewalk_close(struct treeroot
*tree
)
522 * @param rootdir The root directory to start walking the tree.
523 * @param options The options specifying how to walk the tree.
524 * @param func The function callbacks.
527 treewalk(const char *rootdir
, enum treewalk_options options
,
528 struct treewalk_funcs
*func
)
530 struct treeroot
*tree
;
531 struct treenode
*node
;
533 tree
= treewalk_open(rootdir
, options
, func
);
535 /* Breath first visit. */
536 for (node
= treewalk_node(tree
); node
; node
= treewalk_next(tree
))
537 treeroot_visit_node(tree
, node
);
539 treewalk_close(tree
);