summaryrefslogtreecommitdiff
path: root/lib/cycle-check.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cycle-check.c')
-rw-r--r--lib/cycle-check.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/lib/cycle-check.c b/lib/cycle-check.c
new file mode 100644
index 000000000..403dc1615
--- /dev/null
+++ b/lib/cycle-check.c
@@ -0,0 +1,63 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+typedef enum {false = 0, true = 1} bool;
+#endif
+
+#include "cycle-check.h"
+#include "xalloc.h"
+
+#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
+ ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
+ && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)
+
+#define CC_MAGIC 9827862
+
+static inline bool
+is_power_of_two (unsigned int i)
+{
+ return (i & (i - 1)) == 0;
+}
+
+void
+cycle_check_init (struct cycle_check_state *state)
+{
+ state->chdir_counter = 0;
+ state->magic = CC_MAGIC;
+}
+
+/* In traversing a directory hierarchy, call this function once for each
+ descending chdir call, with SB corresponding to the chdir operand.
+ If SB corresponds to a directory that has already been seen,
+ return true to indicate that there is a directory cycle.
+ Note that this is done `lazily', which means that some of
+ the directories in the cycle may be processed twice before
+ the cycle is detected. */
+
+bool
+cycle_check (struct cycle_check_state *state, struct stat const *sb)
+{
+ assert (state->magic == CC_MAGIC);
+
+ /* If the current directory ever happens to be the same
+ as the one we last recorded for the cycle detection,
+ then it's obviously part of a cycle. */
+ if (state->chdir_counter && SAME_INODE (*sb, state->dev_ino))
+ return true;
+
+ /* If the number of `descending' chdir calls is a power of two,
+ record the dev/ino of the current directory. */
+ if (is_power_of_two (++(state->chdir_counter)))
+ {
+ state->dev_ino.st_dev = sb->st_dev;
+ state->dev_ino.st_ino = sb->st_ino;
+ }
+
+ return false;
+}