1# Description: Prints filenames first in the detail view.  Prints user/group
2#              columns when a directory contains different users/groups.
3#
4# Author: Luuk van Baal
5
6diff --git a/src/nnn.c b/src/nnn.c
7index 88263beb..55f32e73 100644
8--- a/src/nnn.c
9+++ b/src/nnn.c
10@@ -390,6 +390,10 @@ typedef struct {
11 } session_header_t;
12 #endif
13
14+static struct {
15+	ushort_t maxnameln, maxsizeln, maxuidln, maxgidln, maxentln, uidln, gidln, printguid;
16+} dtls;
17+
18 /* GLOBALS */
19
20 /* Configuration, contexts */
21@@ -1083,10 +1087,12 @@ static char *getpwname(uid_t uid)
22 	static char *namecache;
23
24 	if (uidcache != uid) {
25+		if (dtls.maxuidln && !dtls.printguid) dtls.printguid = 1;
26 		struct passwd *pw = getpwuid(uid);
27
28 		uidcache = uid;
29 		namecache = pw ? pw->pw_name : NULL;
30+		dtls.uidln = xstrlen(namecache ? namecache : xitoa(uid));
31 	}
32
33 	return namecache ? namecache : xitoa(uid);
34@@ -1098,10 +1104,12 @@ static char *getgrname(gid_t gid)
35 	static char *grpcache;
36
37 	if (gidcache != gid) {
38+		if (dtls.maxgidln && !dtls.printguid) dtls.printguid = 1;
39 		struct group *gr = getgrgid(gid);
40
41 		gidcache = gid;
42 		grpcache = gr ? gr->gr_name : NULL;
43+		dtls.gidln = xstrlen(grpcache ? grpcache : xitoa(gid));
44 	}
45
46 	return grpcache ? grpcache : xitoa(gid);
47@@ -3829,14 +3837,13 @@ static void resetdircolor(int flags)
48  * Max supported str length: NAME_MAX;
49  */
50 #ifdef NOLC
51-static char *unescape(const char *str, uint_t maxcols)
52+static size_t unescape(const char *str, uint_t maxcols)
53 {
54 	char * const wbuf = g_buf;
55 	char *buf = wbuf;
56-
57-	xstrsncpy(wbuf, str, maxcols);
58+	size_t len = xstrsncpy(wbuf, str, maxcols);
59 #else
60-static wchar_t *unescape(const char *str, uint_t maxcols)
61+static size_t unescape(const char *str, uint_t maxcols)
62 {
63 	wchar_t * const wbuf = (wchar_t *)g_buf;
64 	wchar_t *buf = wbuf;
65@@ -3861,7 +3868,7 @@ static wchar_t *unescape(const char *str, uint_t maxcols)
66 		++buf;
67 	}
68
69-	return wbuf;
70+	return len;
71 }
72
73 static off_t get_size(off_t size, off_t *pval, int comp)
74@@ -4122,33 +4129,7 @@ static uchar_t get_color_pair_name_ind(const struct entry *ent, char *pind, int
75 static void printent(const struct entry *ent, uint_t namecols, bool sel)
76 {
77 	char ind = '\0';
78-	int attrs;
79-
80-	if (cfg.showdetail) {
81-		int type = ent->mode & S_IFMT;
82-		char perms[6] = {' ', ' ', (char)('0' + ((ent->mode >> 6) & 7)),
83-				(char)('0' + ((ent->mode >> 3) & 7)),
84-				(char)('0' + (ent->mode & 7)), '\0'};
85-
86-		addch(' ');
87-		attrs = g_state.oldcolor ? (resetdircolor(ent->flags), A_DIM)
88-					 : (fcolors[C_MIS] ? COLOR_PAIR(C_MIS) : 0);
89-		if (attrs)
90-			attron(attrs);
91-
92-		/* Print details */
93-		print_time(&ent->sec);
94-
95-		printw("%s%9s ", perms, (type == S_IFREG || type == S_IFDIR)
96-			? coolsize(cfg.blkorder ? (blkcnt_t)ent->blocks << blk_shift : ent->size)
97-			: (type = (uchar_t)get_detail_ind(ent->mode), (char *)&type));
98-
99-		if (attrs)
100-			attroff(attrs);
101-	}
102-
103-	attrs = 0;
104-
105+	int attrs = 0, namelen;
106 	uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs);
107
108 	addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' ');
109@@ -4173,15 +4154,40 @@ static void printent(const struct entry *ent, uint_t namecols, bool sel)
110 		++namecols;
111
112 #ifndef NOLC
113-	addwstr(unescape(ent->name, namecols));
114+	addwstr((namelen = unescape(ent->name, namecols), (wchar_t *)g_buf));
115 #else
116-	addstr(unescape(ent->name, MIN(namecols, ent->nlen) + 1));
117+	addstr((namelen = unescape(ent->name, MIN(namecols, ent->nlen) + 1), (char *)g_buf));
118 #endif
119
120-	if (attrs)
121+	if (!sel && attrs)
122 		attroff(attrs);
123 	if (ind)
124 		addch(ind);
125+	if (cfg.showdetail) {
126+		int type = ent->mode & S_IFMT;
127+		char perms[6] = {(char)('0' + ((ent->mode >> 6) & 7)),
128+				(char)('0' + ((ent->mode >> 3) & 7)),
129+				(char)('0' + (ent->mode & 7)), ' ', ' ', '\0'}, *size = NULL;
130+
131+		if (attrs)
132+			attron(attrs);
133+		if (!g_state.oldcolor && (type == S_IFDIR || (type == S_IFLNK && ent->flags & DIR_OR_DIRLNK)))
134+			attroff(A_BOLD);
135+		size_t sizelen = (type == S_IFREG || type == S_IFDIR) ? xstrlen(size = coolsize(cfg.blkorder ? ent->blocks << blk_shift : ent->size)) : 1;
136+		printw("%*c%*s%s%s", 1 + MIN(namecols, dtls.maxnameln + (size_t)(ind ? 0 : 1)) - namelen, ' ',
137+				dtls.maxsizeln - sizelen, "", size ? size : (type = (uchar_t)get_detail_ind(ent->mode), (char *)&type), "  ");
138+#ifndef NOUG
139+		if (g_state.uidgid && dtls.printguid) {
140+			addstr(getpwname(ent->uid));
141+			printw("%*c%s", dtls.maxuidln + 1 - dtls.uidln, ' ', getgrname(ent->gid));
142+			printw("%*c", dtls.maxgidln + 2 - dtls.gidln, ' ');
143+		}
144+#endif
145+		addstr(perms);
146+		print_time(&ent->sec);
147+	}
148+	if (attrs)
149+		attroff(attrs);
150 }
151
152 static void savecurctx(char *path, char *curname, int nextctx)
153@@ -6250,18 +6256,6 @@ static void statusbar(char *path)
154 		tocursor();
155 }
156
157-static inline void markhovered(void)
158-{
159-	if (cfg.showdetail && ndents) { /* Reversed block for hovered entry */
160-		tocursor();
161-#ifdef ICONS_ENABLED
162-		addstr(MD_ARROW_FORWARD);
163-#else
164-		addch(' ' | A_REVERSE);
165-#endif
166-	}
167-}
168-
169 static int adjust_cols(int n)
170 {
171 	/* Calculate the number of cols available to print entry name */
172@@ -6269,11 +6263,10 @@ static int adjust_cols(int n)
173 	n -= (g_state.oldcolor ? 0 : 1 + xstrlen(ICON_PADDING_LEFT) + xstrlen(ICON_PADDING_RIGHT));
174 #endif
175 	if (cfg.showdetail) {
176-		/* Fallback to light mode if less than 35 columns */
177-		if (n < 36)
178+		if (n < (dtls.maxentln + 1 - dtls.maxnameln))
179 			cfg.showdetail ^= 1;
180 		else /* 2 more accounted for below */
181-			n -= 32;
182+			n -= (dtls.maxentln - 2 - dtls.maxnameln);
183 	}
184
185 	/* 2 columns for preceding space and indicator */
186@@ -6310,8 +6303,6 @@ static void draw_line(int ncols)
187 	/* Must reset e.g. no files in dir */
188 	if (dir)
189 		attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD);
190-
191-	markhovered();
192 }
193
194 static void redraw(char *path)
195@@ -6419,6 +6410,21 @@ static void redraw(char *path)
196
197 	onscreen = MIN(onscreen + curscroll, ndents);
198
199+	if (cfg.showdetail) {
200+		ushort_t lenbuf = dtls.maxnameln = dtls.maxsizeln = dtls.maxuidln = dtls.maxgidln = dtls.printguid = 0;
201+		for (i = curscroll; i < onscreen; ++i) {
202+			if ((lenbuf = pdents[i].nlen - 1) > dtls.maxnameln) dtls.maxnameln = lenbuf;
203+			if ((lenbuf = xstrlen(coolsize(cfg.blkorder ? pdents[i].blocks << blk_shift : pdents[i].size))) > dtls.maxsizeln) dtls.maxsizeln = lenbuf;
204+#ifndef NOUG
205+			if (g_state.uidgid) {
206+				if ((getpwname(pdents[i].uid), dtls.uidln) > dtls.maxuidln) dtls.maxuidln = dtls.uidln;
207+				if ((getgrname(pdents[i].gid), dtls.gidln) > dtls.maxgidln) dtls.maxgidln = dtls.gidln;
208+			}
209+#endif
210+		}
211+		dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 29) : 26);
212+	}
213+
214 	ncols = adjust_cols(ncols);
215
216 	int len = scanselforpath(path, FALSE);
217@@ -6449,7 +6455,7 @@ static void redraw(char *path)
218 #endif
219 	}
220
221-	markhovered();
222+	statusbar(path);
223 }
224
225 static bool cdprep(char *lastdir, char *lastname, char *path, char *newpath)
226