aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Arun Prakash Jana <engineerarun@gmail.com>2017-04-03 05:05:14 +0530
committerGravatar Arun Prakash Jana <engineerarun@gmail.com>2017-04-03 05:05:14 +0530
commitf466ba543c9dbc59d753420196ddb0d42ca986ce (patch)
treee3fd6b64f47c4470c7b0c1fe283ef9c521ec8989
parent87e0891b8d8b9ac2779d2785ac6fd6e946fdcc5b (diff)
downloadnnn-f466ba543c9dbc59d753420196ddb0d42ca986ce.tar.gz
Show file details
We closely follow the output for stat(1).
-rw-r--r--README.md2
-rw-r--r--config.def.h2
-rw-r--r--nnn.12
-rw-r--r--nnn.c161
4 files changed, 161 insertions, 6 deletions
diff --git a/README.md b/README.md
index b8f7d92..35a2c83 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@ I chose to fork because:
- current item in reverse video
- number of items in current directory
- full name of currently selected file
+ - Show details of the currently selected file
- Directories first
- Sort numeric names in numeric order
- Case-insensitive alphabetic content listing instead of upper case first
@@ -120,6 +121,7 @@ Start nnn (default: current directory):
| `/`, `&` | filter dir contents |
| `c` | show change dir prompt |
| `d` | toggle detail view |
+| `D` | show details of selected file |
| `.` | toggle hide dot files |
| `s` | toggle sort by file size |
| `t` | toggle sort by modified time |
diff --git a/config.def.h b/config.def.h
index cb52960..9ef50c3 100644
--- a/config.def.h
+++ b/config.def.h
@@ -66,6 +66,8 @@ struct key bindings[] = {
{ '.', SEL_TOGGLEDOT, "\0", "\0" },
/* Detailed listing */
{ 'd', SEL_DETAIL, "\0", "\0" },
+ /* File details */
+ { 'D', SEL_STATS, "\0", "\0" },
/* Toggle sort by size */
{ 's', SEL_FSIZE, "\0", "\0" },
/* Toggle sort by time */
diff --git a/nnn.1 b/nnn.1
index 136ac58..4f78f8b 100644
--- a/nnn.1
+++ b/nnn.1
@@ -46,6 +46,8 @@ Change filter (more information below)
Change into the given directory
.It Ic d
Toggle detail view
+.It Ic D
+Show details of selected file
.It Ic \&.
Toggle hide .dot files
.It Ic s
diff --git a/nnn.c b/nnn.c
index 6641a11..9f18584 100644
--- a/nnn.c
+++ b/nnn.c
@@ -17,6 +17,8 @@
#include <string.h>
#include <unistd.h>
#include <time.h>
+#include <pwd.h>
+#include <grp.h>
#ifdef DEBUG
#define DEBUG_FD 8
@@ -62,6 +64,7 @@ enum action {
SEL_CDHOME,
SEL_TOGGLEDOT,
SEL_DETAIL,
+ SEL_STATS,
SEL_FSIZE,
SEL_MTIME,
SEL_REDRAW,
@@ -164,7 +167,7 @@ xstrlcpy(char *dest, const char *src, size_t n)
}
/*
- * The poor man's implementation of memrchr().
+ * The poor man's implementation of memrchr(3).
* We are only looking for '/' in this program.
*/
static void *
@@ -204,7 +207,7 @@ xdirname(const char *path)
#endif
/*
- * The following dirname() implementation does not
+ * The following dirname(3) implementation does not
* change the input. We use a copy of the original.
*
* Modified from the glibc (GNU LGPL) version.
@@ -594,10 +597,7 @@ static void
printent_long(struct entry *ent, int active)
{
static char buf[18];
- static const struct tm *p;
-
- p = localtime(&ent->t);
- strftime(buf, 18, "%b %d %H:%M %Y", p);
+ strftime(buf, 18, "%b %d %H:%M %Y", localtime(&ent->t));
if (active)
attron(A_REVERSE);
@@ -631,6 +631,140 @@ printent_long(struct entry *ent, int active)
attroff(A_REVERSE);
}
+static char
+get_fileind(mode_t mode, char *desc)
+{
+ char c;
+
+ if (S_ISREG(mode)) {
+ c = '-';
+ sprintf(desc, "%s", "regular file");
+ } else if (S_ISDIR(mode)) {
+ c = 'd';
+ sprintf(desc, "%s", "directory");
+ } else if (S_ISBLK(mode)) {
+ c = 'b';
+ sprintf(desc, "%s", "block special device");
+ } else if (S_ISCHR(mode)) {
+ c = 'c';
+ sprintf(desc, "%s", "character special device");
+#ifdef S_ISFIFO
+ } else if (S_ISFIFO(mode)) {
+ c = 'p';
+ sprintf(desc, "%s", "FIFO");
+#endif /* S_ISFIFO */
+#ifdef S_ISLNK
+ } else if (S_ISLNK(mode)) {
+ c = 'l';
+ sprintf(desc, "%s", "symbolic link");
+#endif /* S_ISLNK */
+#ifdef S_ISSOCK
+ } else if (S_ISSOCK(mode)) {
+ c = 's';
+ sprintf(desc, "%s", "socket");
+#endif /* S_ISSOCK */
+#ifdef S_ISDOOR
+ /* Solaris 2.6, etc. */
+ } else if (S_ISDOOR(mode)) {
+ c = 'D';
+ desc[0] = '\0';
+#endif /* S_ISDOOR */
+ } else {
+ /* Unknown type -- possibly a regular file? */
+ c = '?';
+ desc[0] = '\0';
+ }
+
+ return(c);
+}
+
+/* Convert a mode field into "ls -l" type perms field. */
+static char *
+get_lsperms(mode_t mode, char *desc)
+{
+ static const char *rwx[] = {"---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx"};
+ static char bits[11];
+
+ bits[0] = get_fileind(mode, desc);
+ strcpy(&bits[1], rwx[(mode >> 6) & 7]);
+ strcpy(&bits[4], rwx[(mode >> 3) & 7]);
+ strcpy(&bits[7], rwx[(mode & 7)]);
+
+ if (mode & S_ISUID)
+ bits[3] = (mode & S_IXUSR) ? 's' : 'S';
+ if (mode & S_ISGID)
+ bits[6] = (mode & S_IXGRP) ? 's' : 'l';
+ if (mode & S_ISVTX)
+ bits[9] = (mode & S_IXOTH) ? 't' : 'T';
+
+ bits[10] = '\0';
+
+ return(bits);
+}
+
+/*
+ * Follows the stat(1) output closely
+ */
+void
+show_stats(char* fpath, char* fname, struct stat *sb)
+{
+ char buf[40];
+ char *perms = get_lsperms(sb->st_mode, buf);
+
+ clear();
+
+ /* Show file name or 'symlink' -> 'target' */
+ if (perms[0] == 'l') {
+ char symtgt[PATH_MAX];
+ ssize_t len = readlink(fpath, symtgt, PATH_MAX);
+ if (len == -1)
+ printerr(1, "readlink");
+ printw("\n\n File: '%s' -> '%s'", fname, symtgt);
+ } else
+ printw("\n\n File: '%s'", fname);
+
+ /* Show size, blocks, file type */
+ printw("\n Size: %-15llu Blocks: %-10llu IO Block: %-6llu %s",
+ sb->st_size, sb->st_blocks, sb->st_blksize, buf);
+
+ /* Show containing device, inode, hardlink count */
+ sprintf(buf, "%lxh/%lud", sb->st_dev, sb->st_dev);
+ printw("\n Device: %-15s Inode: %-11lu Links: %-9lu",
+ buf, sb->st_ino, sb->st_nlink);
+
+ /* Show major, minor number for block or char device */
+ if (perms[0] == 'b' || perms[0] == 'c')
+ printw(" Device type: %lx,%lx",
+ major(sb->st_rdev), minor(sb->st_rdev));
+
+ /* Show permissions, owner, group */
+ printw("\n Access: 0%d%d%d/%s Uid: (%lu/%s) Gid: (%lu/%s)",
+ (sb->st_mode >> 6) & 7, (sb->st_mode >> 3) & 7, sb->st_mode & 7,
+ perms,
+ sb->st_uid, (getpwuid(sb->st_uid))->pw_name,
+ sb->st_gid, (getgrgid(sb->st_gid))->gr_name);
+
+ /* Show last access time */
+ strftime(buf, 40, "%a %d-%b-%Y %T %z,%Z", localtime(&sb->st_atime));
+ printw("\n\n Access: %s", buf);
+
+ /* Show last modification time */
+ strftime(buf, 40, "%a %d-%b-%Y %T %z,%Z", localtime(&sb->st_mtime));
+ printw("\n Modify: %s", buf);
+
+ /* Show last status change time */
+ strftime(buf, 40, "%a %d-%b-%Y %T %z,%Z", localtime(&sb->st_ctime));
+ printw("\n Change: %s", buf);
+
+ /* Show exit keys */
+ printw("\n\n\n\n < (q/Esc)");
+
+ while (*buf = getch())
+ if (*buf == 'q' || *buf == 27)
+ return;
+}
+
static int
dentfill(char *path, struct entry **dents,
int (*filter)(regex_t *, char *), regex_t *re)
@@ -1036,6 +1170,21 @@ nochange:
if (ndents > 0)
mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
goto begin;
+ case SEL_STATS:
+ {
+ struct stat sb;
+
+ if (ndents > 0)
+ mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
+
+ r = lstat(oldpath, &sb);
+ if (r == -1)
+ printerr(1, "lstat");
+ else
+ show_stats(oldpath, dents[cur].name, &sb);
+
+ goto begin;
+ }
case SEL_FSIZE:
sizeorder = !sizeorder;
mtimeorder = 0;