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 <http://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(struct treenode
*);
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
);
281 treenode_free_down(struct treenode
*node
)
285 if (!node
->down_size
)
288 for (i
= 0; i
< node
->down_used
; i
++)
289 treenode_free_node(node
->down
[i
]);
294 /* treeroot functions. */
297 struct treenode
*rootnode
;
299 struct treenode
*curdir
;
300 struct treenode
*curnode
;
302 enum treewalk_options options
;
303 struct treewalk_funcs func
;
307 treeroot_set_curdir(struct treeroot
*tree
, struct treenode
*node
)
313 treeroot_set_curnode(struct treeroot
*tree
, struct treenode
*node
)
315 tree
->curnode
= node
;
319 treeroot_skip_node(struct treeroot
*tree
, struct treenode
*node
)
322 return tree
->func
.skip(node
);
328 treeroot_fill_node(struct treeroot
*tree
, struct treenode
*dir
)
330 treenode_fill_down(tree
->rootnode
, dir
, tree
->options
);
334 treeroot_sort_node(struct treeroot
*tree
, struct treenode
*dir
)
336 static struct treenode
*down_empty
[] = { NULL
, NULL
};
338 if (dir
->down_used
== 0)
339 dir
->down
= down_empty
;
340 else if (tree
->func
.sort
)
341 tree
->func
.sort(dir
);
343 treenode_sort_down(dir
);
347 treeroot_visit_node(struct treeroot
*tree
, struct treenode
*node
)
349 if (tree
->func
.visit
== NULL
)
352 if (!treeroot_skip_node(tree
, node
))
353 tree
->func
.visit(node
);
357 * Open a new tree to be walked.
359 * @param rootdir The root directory to start walking the tree.
360 * @param options The options specifying how to walk the tree.
361 * @param func The functions callbacks.
364 treewalk_open(const char *rootdir
, enum treewalk_options options
,
365 struct treewalk_funcs
*func
)
367 struct treeroot
*tree
;
368 struct treenode
*root
;
370 tree
= m_malloc(sizeof(struct treeroot
));
372 tree
->options
= options
;
376 tree
->func
= (struct treewalk_funcs
){ };
378 root
= treenode_root_new(rootdir
);
379 treenode_stat(root
, lstat
);
381 if (!treenode_is_dir(root
))
382 ohshit(_("treewalk root %s is not a directory"), rootdir
);
384 treeroot_set_curnode(tree
, root
);
385 tree
->rootnode
= tree
->curdir
= root
;
391 * Return the current node.
393 * This function is only needed if you want to start walking the tree from
394 * the root node. With something like:
397 * struct treeroot *tree;
398 * struct treenode *node;
400 * tree = treewalk_open(...);
401 * for (node = treewalk_node(tree); node; node = treewalk_next(tree))
403 * treewalk_close(tree);
406 * @param tree The tree structure.
409 treewalk_node(struct treeroot
*tree
)
411 return tree
->curnode
;
415 * Return the next node.
417 * This function works basically as an iterator. And will return NULL when
418 * the whole tree has been traversed. When starting it will skip the root
419 * node, so you might want to use treewalk_node() to get that, otherwise
420 * you could use it like this:
423 * struct treeroot *tree;
424 * struct treenode *node;
426 * tree = treewalk_open(...);
427 * while ((node = treewalk_next(tree))
429 * treewalk_close(tree);
432 * @param tree The tree structure.
435 treewalk_next(struct treeroot
*tree
)
437 struct treenode
*node
;
439 node
= tree
->curnode
;
441 /* Handle end of tree. */
445 /* Get next node, descending or sidewide. */
446 if (treenode_is_dir(node
) && !treeroot_skip_node(tree
, node
)) {
447 struct treenode
*dir
;
449 treeroot_fill_node(tree
, node
);
450 treeroot_sort_node(tree
, node
);
451 treeroot_set_curdir(tree
, node
);
453 /* Check for directory loops. */
454 for (dir
= node
->up
; dir
; dir
= dir
->up
) {
455 if (dir
->stat
->st_dev
== node
->stat
->st_dev
&&
456 dir
->stat
->st_ino
== node
->stat
->st_ino
)
460 /* Skip directory loops. */
464 node
= node
->down
[0];
469 /* Back track node, ascending. */
470 while (node
== NULL
) {
471 struct treenode
*olddir
= tree
->curdir
;
473 if (tree
->curdir
->next
) {
474 /* Next entry in the parent directory. */
475 node
= tree
->curdir
->next
;
476 treeroot_set_curdir(tree
, olddir
->up
);
477 treenode_free_down(olddir
);
478 } else if (tree
->curdir
->up
) {
479 /* Next entry in the grand-parent directory. */
480 node
= tree
->curdir
->up
->next
;
481 treeroot_set_curdir(tree
, olddir
->up
->up
);
482 treenode_free_down(olddir
);
483 treenode_free_down(olddir
->up
);
485 /* Otherwise, we're in the rootnode. */
486 treenode_free_down(olddir
);
487 treenode_free_node(olddir
);
491 if (tree
->curdir
== NULL
)
495 treeroot_set_curnode(tree
, node
);
501 * Closes the tree being walked.
503 * It will free any resources previously allocated.
506 treewalk_close(struct treeroot
*tree
)
514 * @param rootdir The root directory to start walking the tree.
515 * @param options The options specifying how to walk the tree.
516 * @param func The function callbacks.
519 treewalk(const char *rootdir
, enum treewalk_options options
,
520 struct treewalk_funcs
*func
)
522 struct treeroot
*tree
;
523 struct treenode
*node
;
525 tree
= treewalk_open(rootdir
, options
, func
);
527 /* Breath first visit. */
528 for (node
= treewalk_node(tree
); node
; node
= treewalk_next(tree
))
529 treeroot_visit_node(tree
, node
);
531 treewalk_close(tree
);