diff --git a/configure.ac b/configure.ac index f238137..9cec626 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,11 @@ AC_CHECK_FUNCS( AC_CHECK_FUNCS(statfs) +AC_CHECK_HEADERS([sys/attr.h]) + +AC_CHECK_FUNCS([getattrlist]) + +AC_CHECK_DECLS([ATTR_CMNEXT_NOFIRMLINKPATH], [], [], [[#include ]]) # Look for ncurses library to link to ncurses=auto diff --git a/src/browser.c b/src/browser.c index 3a9971f..4306cbd 100644 --- a/src/browser.c +++ b/src/browser.c @@ -119,6 +119,7 @@ static void browse_draw_flag(struct dir *n, int *x) { n->flags & FF_SERR ? '.' : n->flags & FF_OTHFS ? '>' : n->flags & FF_KERNFS ? '^' : + n->flags & FF_FRMLNK ? 'F' : n->flags & FF_HLNKC ? 'H' : !(n->flags & FF_FILE || n->flags & FF_DIR) ? '@' : diff --git a/src/dir_export.c b/src/dir_export.c index f602421..dd96721 100644 --- a/src/dir_export.c +++ b/src/dir_export.c @@ -116,7 +116,7 @@ static void output_info(struct dir *d, const char *name, struct dir_ext *e) { if(d->flags & FF_ERR) fputs(",\"read_error\":true", stream); /* excluded/error'd files are "unknown" with respect to the "notreg" field. */ - if(!(d->flags & (FF_DIR|FF_FILE|FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS))) + if(!(d->flags & (FF_DIR|FF_FILE|FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS|FF_FRMLNK))) fputs(",\"notreg\":true", stream); if(d->flags & FF_EXL) fputs(",\"excluded\":\"pattern\"", stream); @@ -124,6 +124,8 @@ static void output_info(struct dir *d, const char *name, struct dir_ext *e) { fputs(",\"excluded\":\"othfs\"", stream); else if(d->flags & FF_KERNFS) fputs(",\"excluded\":\"kernfs\"", stream); + else if(d->flags & FF_FRMLNK) + fputs(",\"excluded\":\"frmlnk\"", stream); fputc('}', stream); } diff --git a/src/dir_import.c b/src/dir_import.c index b3aeb7b..142b5e4 100644 --- a/src/dir_import.c +++ b/src/dir_import.c @@ -482,6 +482,8 @@ static int iteminfo(void) { ctx->buf_dir->flags |= FF_OTHFS; else if(strcmp(ctx->val, "kernfs") == 0) ctx->buf_dir->flags |= FF_KERNFS; + else if(strcmp(ctx->val, "frmlnk") == 0) + ctx->buf_dir->flags |= FF_FRMLNK; else ctx->buf_dir->flags |= FF_EXL; } else if(strcmp(ctx->val, "notreg") == 0) { /* notreg */ diff --git a/src/dir_scan.c b/src/dir_scan.c index 74c123a..ea3c267 100644 --- a/src/dir_scan.c +++ b/src/dir_scan.c @@ -34,6 +34,10 @@ #include #include +#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH +#include +#endif + #if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS #include #include @@ -241,6 +245,25 @@ static int dir_scan_item(const char *name) { } #endif +#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH + if(!follow_firmlinks) { + struct attrlist list = { + .bitmapcount = ATTR_BIT_MAP_COUNT, + .forkattr = ATTR_CMNEXT_NOFIRMLINKPATH, + }; + struct { + uint32_t length; + attrreference_t reference; + char extra[PATH_MAX]; + } __attribute__((aligned(4), packed)) attributes; + if (getattrlist(name, &list, &attributes, sizeof(attributes), FSOPT_ATTR_CMN_EXTENDED) == -1) { + buf_dir->flags |= FF_ERR; + dir_setlasterr(dir_curpath); + } else if (strcmp(dir_curpath, (char *)&attributes.reference + attributes.reference.attr_dataoffset)) + buf_dir->flags |= FF_FRMLNK; + } +#endif + if(!(buf_dir->flags & (FF_ERR|FF_EXL))) { if(follow_symlinks && S_ISLNK(st.st_mode) && !stat(name, &stl) && !S_ISDIR(stl.st_mode)) stat_to_dir(&stl); @@ -248,14 +271,14 @@ static int dir_scan_item(const char *name) { stat_to_dir(&st); } - if(cachedir_tags && (buf_dir->flags & FF_DIR) && !(buf_dir->flags & (FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS))) + if(cachedir_tags && (buf_dir->flags & FF_DIR) && !(buf_dir->flags & (FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS|FF_FRMLNK))) if(has_cachedir_tag(name)) { buf_dir->flags |= FF_EXL; buf_dir->size = buf_dir->asize = 0; } /* Recurse into the dir or output the item */ - if(buf_dir->flags & FF_DIR && !(buf_dir->flags & (FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS))) + if(buf_dir->flags & FF_DIR && !(buf_dir->flags & (FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS|FF_FRMLNK))) fail = dir_scan_recurse(name); else if(buf_dir->flags & FF_DIR) { if(dir_output.item(buf_dir, name, buf_ext) || dir_output.item(NULL, 0, NULL)) { diff --git a/src/global.h b/src/global.h index de7a9c2..afb9c5e 100644 --- a/src/global.h +++ b/src/global.h @@ -52,6 +52,7 @@ #define FF_BSEL 0x80 /* selected */ #define FF_EXT 0x100 /* extended struct available */ #define FF_KERNFS 0x200 /* excluded because it was a Linux pseudo filesystem */ +#define FF_FRMLNK 0x400 /* excluded because it was a firmlink */ /* Program states */ #define ST_CALC 0 @@ -106,6 +107,8 @@ extern int confirm_quit; extern int extended_info; /* flag whether we want to follow symlinks */ extern int follow_symlinks; +/* flag whether we want to follow firmlinks */ +extern int follow_firmlinks; /* handle input from keyboard and update display */ int input_handle(int); diff --git a/src/main.c b/src/main.c index cc7d43b..13b6de8 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,7 @@ long update_delay = 100; int cachedir_tags = 0; int extended_info = 0; int follow_symlinks = 0; +int follow_firmlinks = 0; int confirm_quit = 0; static int min_rows = 17, min_cols = 60; @@ -138,6 +139,7 @@ static void argv_parse(int argc, char **argv) { { 'L', 0, "-L,--follow-symlinks" }, { 'C', 0, "--exclude-caches" }, { 2, 0, "--exclude-kernfs" }, + { 3, 0, "--follow-firmlinks" }, { 's', 0, "--si" }, { 'Q', 0, "--confirm-quit" }, { 'c', 1, "--color" }, @@ -169,6 +171,9 @@ static void argv_parse(int argc, char **argv) { printf(" --exclude-caches Exclude directories containing CACHEDIR.TAG\n"); #if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS printf(" --exclude-kernfs Exclude Linux pseudo filesystems (procfs,sysfs,cgroup,...)\n"); +#endif +#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH + printf(" --follow-firmlinks Follow firmlinks on macOS\n"); #endif printf(" --confirm-quit Confirm quitting ncdu\n"); printf(" --color SCHEME Set color scheme (off/dark)\n"); @@ -205,6 +210,13 @@ static void argv_parse(int argc, char **argv) { #else fprintf(stderr, "This feature is not supported on your platform\n"); exit(1); +#endif + case 3 : +#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH + follow_firmlinks = 1; break; +#else + fprintf(stderr, "This feature is not supported on your platform\n"); + exit(1); #endif case 'c': if(strcmp(val, "off") == 0) { uic_theme = 0; }