#include <sys/param.h>
#include <sys/stat.h>
+#ifdef __APPLE__
+#include <sys/acl.h>
+#include <sys/xattr.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <TargetConditionals.h>
+#include <membership.h>
+#include <membershipPriv.h>
+#include <uuid/uuid.h>
+#endif
#include <err.h>
#include <errno.h>
#include <fts.h>
#include <math.h>
#include <langinfo.h>
+#include <libutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termcap.h>
#include <signal.h>
#endif
+#include <stdint.h> /* intmax_t */
+#include <assert.h>
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
#include "ls.h"
#include "extern.h"
#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
-#define KILO_SZ(n) (n)
-#define MEGA_SZ(n) ((n) * (n))
-#define GIGA_SZ(n) ((n) * (n) * (n))
-#define TERA_SZ(n) ((n) * (n) * (n) * (n))
-#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
-
-#define KILO_2_SZ (KILO_SZ(1024ULL))
-#define MEGA_2_SZ (MEGA_SZ(1024ULL))
-#define GIGA_2_SZ (GIGA_SZ(1024ULL))
-#define TERA_2_SZ (TERA_SZ(1024ULL))
-#define PETA_2_SZ (PETA_SZ(1024ULL))
-
-static u_int64_t vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
-
-typedef enum {
- NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX
-} unit_t;
-static unit_t unit_adjust(off_t *);
-
-static int unitp[] = {NONE, KILO, MEGA, GIGA, TERA, PETA};
-
#ifdef COLORLS
/* Most of these are taken from <sys/stat.h> */
typedef enum Colors {
{
FTSENT *p;
+ assert(dp);
+ if (COMPAT_MODE("bin/ls", "Unix2003") && (dp->list != NULL)) {
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+ (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
+ }
+
for (p = dp->list; p; p = p->fts_link) {
if (IS_NOPRINT(p))
continue;
else if (f_nonprint)
return prn_printable(name);
else
- return printf("%s", name);
+ return prn_normal(name);
+}
+
+/*
+ * print access control list
+ */
+static struct {
+ acl_perm_t perm;
+ char *name;
+ int flags;
+#define ACL_PERM_DIR (1<<0)
+#define ACL_PERM_FILE (1<<1)
+} acl_perms[] = {
+ {ACL_READ_DATA, "read", ACL_PERM_FILE},
+ {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR},
+ {ACL_WRITE_DATA, "write", ACL_PERM_FILE},
+ {ACL_ADD_FILE, "add_file", ACL_PERM_DIR},
+ {ACL_EXECUTE, "execute", ACL_PERM_FILE},
+ {ACL_SEARCH, "search", ACL_PERM_DIR},
+ {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_APPEND_DATA, "append", ACL_PERM_FILE},
+ {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR},
+ {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR},
+ {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR},
+ {0, NULL, 0}
+};
+
+static struct {
+ acl_flag_t flag;
+ char *name;
+ int flags;
+} acl_flags[] = {
+ {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR},
+ {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR},
+ {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR},
+ {0, NULL, 0}
+};
+
+static char *
+uuid_to_name(uuid_t *uu)
+{
+ int type;
+ char *name = NULL;
+ char *recname = NULL;
+
+#define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */
+ name = (char *) malloc(MAXNAMETAG);
+
+ if (NULL == name) {
+ err(1, "malloc");
+ }
+
+ if (f_numericonly) {
+ goto errout;
+ }
+
+ if (mbr_identifier_translate(ID_TYPE_UUID, *uu, sizeof(*uu), ID_TYPE_NAME, (void **) &recname, &type)) {
+ goto errout;
+ }
+
+ snprintf(name, MAXNAMETAG, "%s:%s", (type == MBR_REC_TYPE_USER ? "user" : "group"), recname);
+ free(recname);
+
+ return name;
+errout:
+ uuid_unparse_upper(*uu, name);
+
+ return name;
+}
+
+static void
+printxattr(DISPLAY *dp, int count, char *buf, int sizes[])
+{
+ for (int i = 0; i < count; i++) {
+ putchar('\t');
+ printname(buf);
+ putchar('\t');
+ printsize(dp->s_size, sizes[i]);
+ putchar('\n');
+ buf += strlen(buf) + 1;
+ }
+}
+
+static void
+printacl(acl_t acl, int isdir)
+{
+ acl_entry_t entry = NULL;
+ int index;
+ uuid_t *applicable;
+ char *name = NULL;
+ acl_tag_t tag;
+ acl_flagset_t flags;
+ acl_permset_t perms;
+ char *type;
+ int i, first;
+
+
+ for (index = 0;
+ acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;
+ index++) {
+ if (acl_get_tag_type(entry, &tag) != 0)
+ continue;
+ if (acl_get_flagset_np(entry, &flags) != 0)
+ continue;
+ if (acl_get_permset(entry, &perms) != 0)
+ continue;
+ if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL)
+ continue;
+ name = uuid_to_name(applicable);
+ acl_free(applicable);
+ switch(tag) {
+ case ACL_EXTENDED_ALLOW:
+ type = "allow";
+ break;
+ case ACL_EXTENDED_DENY:
+ type = "deny";
+ break;
+ default:
+ type = "unknown";
+ }
+
+ (void)printf(" %d: %s%s %s ",
+ index,
+ name,
+ acl_get_flag_np(flags, ACL_ENTRY_INHERITED) ? " inherited" : "",
+ type);
+
+ if (name)
+ free(name);
+
+ for (i = 0, first = 0; acl_perms[i].name != NULL; i++) {
+ if (acl_get_perm_np(perms, acl_perms[i].perm) == 0)
+ continue;
+ if (!(acl_perms[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
+ continue;
+ (void)printf("%s%s", first++ ? "," : "", acl_perms[i].name);
+ }
+ for (i = 0; acl_flags[i].name != NULL; i++) {
+ if (acl_get_flag_np(flags, acl_flags[i].flag) == 0)
+ continue;
+ if (!(acl_flags[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
+ continue;
+ (void)printf("%s%s", first++ ? "," : "", acl_flags[i].name);
+ }
+
+ (void)putchar('\n');
+ }
+
}
void
if (IS_NOPRINT(p))
continue;
sp = p->fts_statp;
- if (f_inode)
+ if (f_inode)
+#if _DARWIN_FEATURE_64_BIT_INODE
+ (void)printf("%*llu ", dp->s_inode, (u_quad_t)sp->st_ino);
+#else
(void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
+#endif
if (f_size)
(void)printf("%*qu ",
dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize));
strmode(sp->st_mode, buf);
np = p->fts_pointer;
- (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink,
- sp->st_nlink, dp->s_user, np->user, dp->s_group,
- np->group);
+#ifdef __APPLE__
+ buf[10] = '\0'; /* make +/@ abut the mode */
+ char str[2] = { np->mode_suffix, '\0' };
+#endif /* __APPLE__ */
+ if (f_group && f_owner) { /* means print neither */
+#ifdef __APPLE__
+ (void)printf("%s%s %*u ", buf, str, dp->s_nlink,
+ sp->st_nlink);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u ", buf, dp->s_nlink,
+ sp->st_nlink);
+#endif /* __APPLE__ */
+ }
+ else if (f_group) {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink,
+ sp->st_nlink, dp->s_group, np->group);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u %-*s ", buf, dp->s_nlink,
+ sp->st_nlink, dp->s_group, np->group);
+#endif /* __APPLE__ */
+ }
+ else if (f_owner) {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u %-*s ", buf, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user);
+#endif /* __APPLE__ */
+ }
+ else {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s %-*s ", buf, str, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user, dp->s_group,
+ np->group);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user, dp->s_group,
+ np->group);
+#endif /* ! __APPLE__ */
+ }
if (f_flags)
(void)printf("%-*s ", dp->s_flags, np->flags);
-#ifndef __APPLE__
- if (f_lomac)
- (void)printf("%-*s ", dp->s_lattr, np->lattr);
-#endif /* __APPLE__ */
if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
(void)printf("%3d, 0x%08x ",
printtime(sp->st_atime);
else if (f_statustime)
printtime(sp->st_ctime);
+ else if (f_birthtime)
+ printtime(sp->st_birthtime);
else
printtime(sp->st_mtime);
#ifdef COLORLS
if (S_ISLNK(sp->st_mode))
printlink(p);
(void)putchar('\n');
+#ifdef __APPLE__
+ if (np->xattr_count && f_xattr) {
+ printxattr(dp, np->xattr_count, np->xattr_names, np->xattr_sizes);
+ }
+ if (np->acl != NULL && f_acl) {
+ printacl(np->acl, S_ISDIR(sp->st_mode));
+ }
+#endif /* __APPLE__ */
}
}
* Have to do random access in the linked list -- build a table
* of pointers.
*/
- if (dp->entries > lastentries) {
+ if ((lastentries == -1) || (dp->entries > lastentries)) {
lastentries = dp->entries;
- if ((array =
- realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
+ if ((array = realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
warn(NULL);
printscol(dp);
+ return;
}
}
+ memset(array, 0, dp->entries * sizeof(FTSENT *));
for (p = dp->list, num = 0; p; p = p->fts_link)
if (p->fts_number != NO_PRINT)
array[num++] = p;
if (num % numcols)
++numrows;
+ assert(dp->list);
if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
(void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
if (!f_sortacross)
base = row;
for (col = 0, chcnt = 0; col < numcols; ++col) {
- chcnt += printaname(array[base], dp->s_inode,
- dp->s_block);
+ assert(base < dp->entries);
+ chcnt += printaname(array[base], dp->s_inode, dp->s_block);
if (f_sortacross)
base++;
else
sp = p->fts_statp;
chcnt = 0;
if (f_inode)
+#if _DARWIN_FEATURE_64_BIT_INODE
+ chcnt += printf("%*llu ", (int)inodefield, (u_quad_t)sp->st_ino);
+#else
chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
+#endif
if (f_size)
chcnt += printf("%*qu ",
(int)sizefield, (u_int64_t)howmany(sp->st_blocks, blocksize));
if (f_sectime)
/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
+ else if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ if (ftime + SIXMONTHS > now && ftime <= now)
+ /* mmm dd hh:mm || dd mmm hh:mm */
+ format = d_first ? "%e %b %R " : "%b %e %R ";
+ else
+ /* mmm dd yyyy || dd mmm yyyy */
+ format = d_first ? "%e %b %Y " : "%b %e %Y ";
+ }
else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
/* mmm dd hh:mm || dd mmm hh:mm */
format = d_first ? "%e %b %R " : "%b %e %R ";
static void
printsize(size_t width, off_t bytes)
{
- unit_t unit;
-
- if (f_humanval) {
- unit = unit_adjust(&bytes);
-
- if (bytes == 0)
- (void)printf("%*s ", width, "0B");
- else
- (void)printf("%*lld%c ", width - 1, bytes,
- "BKMGTPE"[unit]);
- } else
- (void)printf("%*lld ", width, bytes);
-}
-/*
- * Output in "human-readable" format. Uses 3 digits max and puts
- * unit suffixes at the end. Makes output compact and easy to read,
- * especially on huge disks.
- *
- */
-unit_t
-unit_adjust(off_t *val)
-{
- double abval;
- unit_t unit;
- unsigned int unit_sz;
-
- abval = fabs((double)*val);
-
- unit_sz = abval ? ilogb(abval) / 10 : 0;
-
- if (unit_sz >= UNIT_MAX) {
- unit = NONE;
- } else {
- unit = unitp[unit_sz];
- *val /= (double)vals_base2[unit_sz];
- }
+ if (f_humanval) {
+ char buf[5];
- return (unit);
+ humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ (void)printf("%5s ", buf);
+ } else
+ (void)printf("%*jd ", (u_int)width, (intmax_t)bytes);
}