xref: /openbsd-src/bin/ls/print.c (revision ba906cc60a26c59b99faad7f7bfb01641b9d63ca)
1*ba906cc6Smillert /*	$OpenBSD: print.c,v 1.41 2024/03/27 14:44:52 millert Exp $	*/
24bd83475Smillert /*	$NetBSD: print.c,v 1.15 1996/12/11 03:25:39 thorpej Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1989, 1993, 1994
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Michael Fischbein.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
1929295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
20df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
21df930be7Sderaadt  *    without specific prior written permission.
22df930be7Sderaadt  *
23df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt  * SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt 
36b9fc9a72Sderaadt #include <sys/types.h>
37df930be7Sderaadt #include <sys/stat.h>
38df930be7Sderaadt 
39df930be7Sderaadt #include <err.h>
40df930be7Sderaadt #include <errno.h>
41df930be7Sderaadt #include <fts.h>
42df930be7Sderaadt #include <grp.h>
43df930be7Sderaadt #include <pwd.h>
44df930be7Sderaadt #include <stdio.h>
45df930be7Sderaadt #include <stdlib.h>
46df930be7Sderaadt #include <string.h>
47df930be7Sderaadt #include <time.h>
48df930be7Sderaadt #include <unistd.h>
49b9fc9a72Sderaadt #include <limits.h>
5091ea06e0Stedu #include <util.h>
51df930be7Sderaadt 
52df930be7Sderaadt #include "ls.h"
53df930be7Sderaadt #include "extern.h"
54df930be7Sderaadt 
554442fdb7Skrw static int	printaname(FTSENT *, int, int);
56c72b5b24Smillert static void	printlink(FTSENT *);
577e8a4e4eSkrw static void	printsize(int, off_t);
58c72b5b24Smillert static void	printtime(time_t);
594442fdb7Skrw static int	printtype(mode_t);
60c72b5b24Smillert static int	compute_columns(DISPLAY *, int *);
61df930be7Sderaadt 
62df930be7Sderaadt #define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
63df930be7Sderaadt 
6464a7b209Sguenther #define	DATELEN		64
65f7055df5Smillert 
66f7055df5Smillert #define	SECSPERDAY	(24 * 60 * 60)
67f7055df5Smillert #define	SIXMONTHS	(SECSPERDAY * 365 / 2)
6864a7b209Sguenther 
69df930be7Sderaadt void
printscol(DISPLAY * dp)7028416801Sderaadt printscol(DISPLAY *dp)
71df930be7Sderaadt {
72df930be7Sderaadt 	FTSENT *p;
73df930be7Sderaadt 
74df930be7Sderaadt 	for (p = dp->list; p; p = p->fts_link) {
75df930be7Sderaadt 		if (IS_NOPRINT(p))
76df930be7Sderaadt 			continue;
77df930be7Sderaadt 		(void)printaname(p, dp->s_inode, dp->s_block);
78df930be7Sderaadt 		(void)putchar('\n');
79df930be7Sderaadt 	}
80df930be7Sderaadt }
81df930be7Sderaadt 
82df930be7Sderaadt void
printlong(DISPLAY * dp)8328416801Sderaadt printlong(DISPLAY *dp)
84df930be7Sderaadt {
85df930be7Sderaadt 	struct stat *sp;
86df930be7Sderaadt 	FTSENT *p;
87df930be7Sderaadt 	NAMES *np;
88df930be7Sderaadt 	char buf[20];
89df930be7Sderaadt 
90bc2593c5Smillert 	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
91bc2593c5Smillert 	    (f_longform || f_size))
923e12fe88Sguenther 		(void)printf("total %llu\n", howmany(dp->btotal, blocksize));
93df930be7Sderaadt 
94df930be7Sderaadt 	for (p = dp->list; p; p = p->fts_link) {
95df930be7Sderaadt 		if (IS_NOPRINT(p))
96df930be7Sderaadt 			continue;
97df930be7Sderaadt 		sp = p->fts_statp;
98df930be7Sderaadt 		if (f_inode)
996be702bbSderaadt 			(void)printf("%*llu ", dp->s_inode,
1006be702bbSderaadt 			    (unsigned long long)sp->st_ino);
101df930be7Sderaadt 		if (f_size)
1027e8a4e4eSkrw 			(void)printf("%*lld ", dp->s_block,
1037e8a4e4eSkrw 			    howmany((long long)sp->st_blocks, blocksize));
104df930be7Sderaadt 		(void)strmode(sp->st_mode, buf);
105df930be7Sderaadt 		np = p->fts_pointer;
106947f7ba6Smillert 		(void)printf("%s %*u ", buf, dp->s_nlink, sp->st_nlink);
107947f7ba6Smillert 		if (!f_grouponly)
108947f7ba6Smillert 			(void)printf("%-*s  ", dp->s_user, np->user);
109947f7ba6Smillert 		(void)printf("%-*s  ", dp->s_group, np->group);
110df930be7Sderaadt 		if (f_flags)
111df930be7Sderaadt 			(void)printf("%-*s ", dp->s_flags, np->flags);
112df930be7Sderaadt 		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
11309b34e9cSschwarze 			(void)printf("%*u, %*u ",
11409b34e9cSschwarze 			    dp->s_major, major(sp->st_rdev),
11509b34e9cSschwarze 			    dp->s_minor, minor(sp->st_rdev));
116df930be7Sderaadt 		else
11791ea06e0Stedu 			printsize(dp->s_size, sp->st_size);
118df930be7Sderaadt 		if (f_accesstime)
119df930be7Sderaadt 			printtime(sp->st_atime);
120df930be7Sderaadt 		else if (f_statustime)
121df930be7Sderaadt 			printtime(sp->st_ctime);
122df930be7Sderaadt 		else
123df930be7Sderaadt 			printtime(sp->st_mtime);
124b6203726Sschwarze 		(void)mbsprint(p->fts_name, 1);
125b94f2f87Sderaadt 		if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
126df930be7Sderaadt 			(void)printtype(sp->st_mode);
127df930be7Sderaadt 		if (S_ISLNK(sp->st_mode))
128df930be7Sderaadt 			printlink(p);
129df930be7Sderaadt 		(void)putchar('\n');
130df930be7Sderaadt 	}
131df930be7Sderaadt }
132df930be7Sderaadt 
133f296455fSespie static int
compute_columns(DISPLAY * dp,int * pnum)13428416801Sderaadt compute_columns(DISPLAY *dp, int *pnum)
135f296455fSespie {
136f296455fSespie 	int colwidth;
137f296455fSespie 	extern int termwidth;
138b60c9e49Sespie 	int mywidth;
139f296455fSespie 
140f296455fSespie 	colwidth = dp->maxlen;
141f296455fSespie 	if (f_inode)
142f296455fSespie 		colwidth += dp->s_inode + 1;
143f296455fSespie 	if (f_size)
144f296455fSespie 		colwidth += dp->s_block + 1;
145f296455fSespie 	if (f_type || f_typedir)
146f296455fSespie 		colwidth += 1;
147f296455fSespie 
148f296455fSespie 	colwidth += 1;
149b60c9e49Sespie 	mywidth = termwidth + 1;	/* no extra space for last column */
150f296455fSespie 
151b60c9e49Sespie 	if (mywidth < 2 * colwidth) {
152f296455fSespie 		printscol(dp);
153f296455fSespie 		return (0);
154f296455fSespie 	}
155f296455fSespie 
156b60c9e49Sespie 	*pnum = mywidth / colwidth;
157b60c9e49Sespie 	return (mywidth / *pnum);		/* spread out if possible */
158f296455fSespie }
159f296455fSespie 
160df930be7Sderaadt void
printcol(DISPLAY * dp)16128416801Sderaadt printcol(DISPLAY *dp)
162df930be7Sderaadt {
163df930be7Sderaadt 	static FTSENT **array;
164df930be7Sderaadt 	static int lastentries = -1;
165df930be7Sderaadt 	FTSENT *p;
1664bd83475Smillert 	int base, chcnt, col, colwidth, num;
1674bd83475Smillert 	int numcols, numrows, row;
168df930be7Sderaadt 
16978803e9cSespie 	if ((colwidth = compute_columns(dp, &numcols)) == 0)
17078803e9cSespie 		return;
171df930be7Sderaadt 	/*
172df930be7Sderaadt 	 * Have to do random access in the linked list -- build a table
173df930be7Sderaadt 	 * of pointers.
174df930be7Sderaadt 	 */
175df930be7Sderaadt 	if (dp->entries > lastentries) {
176e2adce4cSderaadt 		FTSENT **a;
177e2adce4cSderaadt 
178c0bbadaaStedu 		if ((a = reallocarray(array, dp->entries, sizeof(FTSENT *))) ==
179381b91ebSderaadt 		    NULL) {
180381b91ebSderaadt 			free(array);
181381b91ebSderaadt 			array = NULL;
182381b91ebSderaadt 			dp->entries = 0;
183381b91ebSderaadt 			lastentries = -1;
184df930be7Sderaadt 			warn(NULL);
185df930be7Sderaadt 			printscol(dp);
186e2adce4cSderaadt 			return;
187df930be7Sderaadt 		}
188e2adce4cSderaadt 		lastentries = dp->entries;
189e2adce4cSderaadt 		array = a;
190df930be7Sderaadt 	}
191df930be7Sderaadt 	for (p = dp->list, num = 0; p; p = p->fts_link)
192df930be7Sderaadt 		if (p->fts_number != NO_PRINT)
193df930be7Sderaadt 			array[num++] = p;
194df930be7Sderaadt 
195df930be7Sderaadt 	numrows = num / numcols;
196df930be7Sderaadt 	if (num % numcols)
197df930be7Sderaadt 		++numrows;
198df930be7Sderaadt 
199bc2593c5Smillert 	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
200bc2593c5Smillert 	    (f_longform || f_size))
2013e12fe88Sguenther 		(void)printf("total %llu\n", howmany(dp->btotal, blocksize));
202df930be7Sderaadt 	for (row = 0; row < numrows; ++row) {
203b60c9e49Sespie 		for (base = row, col = 0;;) {
2044bd83475Smillert 			chcnt = printaname(array[base], dp->s_inode, dp->s_block);
205df930be7Sderaadt 			if ((base += numrows) >= num)
206df930be7Sderaadt 				break;
207b60c9e49Sespie 			if (++col == numcols)
208b60c9e49Sespie 				break;
2094bd83475Smillert 			while (chcnt++ < colwidth)
2104bd83475Smillert 				putchar(' ');
211df930be7Sderaadt 		}
212df930be7Sderaadt 		(void)putchar('\n');
213df930be7Sderaadt 	}
214df930be7Sderaadt }
215df930be7Sderaadt 
216df930be7Sderaadt /*
217df930be7Sderaadt  * print [inode] [size] name
218df930be7Sderaadt  * return # of characters printed, no trailing characters.
219df930be7Sderaadt  */
220df930be7Sderaadt static int
printaname(FTSENT * p,int inodefield,int sizefield)2214442fdb7Skrw printaname(FTSENT *p, int inodefield, int sizefield)
222df930be7Sderaadt {
223df930be7Sderaadt 	struct stat *sp;
224df930be7Sderaadt 	int chcnt;
225df930be7Sderaadt 
226df930be7Sderaadt 	sp = p->fts_statp;
227df930be7Sderaadt 	chcnt = 0;
228df930be7Sderaadt 	if (f_inode)
2294442fdb7Skrw 		chcnt += printf("%*llu ", inodefield,
2306be702bbSderaadt 		    (unsigned long long)sp->st_ino);
231df930be7Sderaadt 	if (f_size)
2324442fdb7Skrw 		chcnt += printf("%*lld ", sizefield,
2337e8a4e4eSkrw 		    howmany((long long)sp->st_blocks, blocksize));
234b6203726Sschwarze 	chcnt += mbsprint(p->fts_name, 1);
235b94f2f87Sderaadt 	if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
236df930be7Sderaadt 		chcnt += printtype(sp->st_mode);
237df930be7Sderaadt 	return (chcnt);
238df930be7Sderaadt }
239df930be7Sderaadt 
240df930be7Sderaadt static void
printtime(time_t ftime)24128416801Sderaadt printtime(time_t ftime)
242df930be7Sderaadt {
24364a7b209Sguenther 	char f_date[DATELEN];
244*ba906cc6Smillert 	struct tm *tm;
24564a7b209Sguenther 	static time_t now;
24664a7b209Sguenther 	static int now_set = 0;
247df930be7Sderaadt 
24864a7b209Sguenther 	if (! now_set) {
24964a7b209Sguenther 		now = time(NULL);
25064a7b209Sguenther 		now_set = 1;
251bb325d81Sguenther 	}
252df930be7Sderaadt 
25364a7b209Sguenther 	/*
25464a7b209Sguenther 	 * convert time to string, and print
25564a7b209Sguenther 	 */
256*ba906cc6Smillert 	if ((tm = localtime(&ftime)) == NULL) {
257*ba906cc6Smillert 		/* Invalid time stamp, just display the epoch. */
258*ba906cc6Smillert 		ftime = 0;
259*ba906cc6Smillert 		tm = localtime(&ftime);
260*ba906cc6Smillert 	}
26164a7b209Sguenther 	if (strftime(f_date, sizeof(f_date), f_sectime ? "%b %e %H:%M:%S %Y" :
26264a7b209Sguenther 	    (ftime <= now - SIXMONTHS || ftime > now) ? "%b %e  %Y" :
263*ba906cc6Smillert 	    "%b %e %H:%M", tm) == 0)
26464a7b209Sguenther 		f_date[0] = '\0';
26564a7b209Sguenther 
26664a7b209Sguenther 	printf("%s ", f_date);
267df930be7Sderaadt }
268df930be7Sderaadt 
269b94f2f87Sderaadt void
printacol(DISPLAY * dp)27028416801Sderaadt printacol(DISPLAY *dp)
271b94f2f87Sderaadt {
272b94f2f87Sderaadt 	FTSENT *p;
273b94f2f87Sderaadt 	int chcnt, col, colwidth;
274b94f2f87Sderaadt 	int numcols;
275b94f2f87Sderaadt 
276f296455fSespie 	if ( (colwidth = compute_columns(dp, &numcols)) == 0)
277b94f2f87Sderaadt 		return;
278b94f2f87Sderaadt 
279bc2593c5Smillert 	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
280bc2593c5Smillert 	    (f_longform || f_size))
2813e12fe88Sguenther 		(void)printf("total %llu\n", howmany(dp->btotal, blocksize));
28232735195Sespie 	col = 0;
283b94f2f87Sderaadt 	for (p = dp->list; p; p = p->fts_link) {
284b94f2f87Sderaadt 		if (IS_NOPRINT(p))
285b94f2f87Sderaadt 			continue;
286b94f2f87Sderaadt 		if (col >= numcols) {
287b60c9e49Sespie 			col = 0;
288b94f2f87Sderaadt 			(void)putchar('\n');
289b94f2f87Sderaadt 		}
290b94f2f87Sderaadt 		chcnt = printaname(p, dp->s_inode, dp->s_block);
291b60c9e49Sespie 		col++;
292b60c9e49Sespie 		if (col < numcols)
293b94f2f87Sderaadt 			while (chcnt++ < colwidth)
294b94f2f87Sderaadt 				(void)putchar(' ');
295b94f2f87Sderaadt 	}
296b94f2f87Sderaadt 	(void)putchar('\n');
297b94f2f87Sderaadt }
298b94f2f87Sderaadt 
299b94f2f87Sderaadt void
printstream(DISPLAY * dp)30028416801Sderaadt printstream(DISPLAY *dp)
301b94f2f87Sderaadt {
302b94f2f87Sderaadt 	extern int termwidth;
303b94f2f87Sderaadt 	FTSENT *p;
304b94f2f87Sderaadt 	int col;
305b94f2f87Sderaadt 	int extwidth;
306b94f2f87Sderaadt 
307b94f2f87Sderaadt 	extwidth = 0;
308b94f2f87Sderaadt 	if (f_inode)
309b94f2f87Sderaadt 		extwidth += dp->s_inode + 1;
310b94f2f87Sderaadt 	if (f_size)
311b94f2f87Sderaadt 		extwidth += dp->s_block + 1;
312b94f2f87Sderaadt 	if (f_type)
313b94f2f87Sderaadt 		extwidth += 1;
314b94f2f87Sderaadt 
315b94f2f87Sderaadt 	for (col = 0, p = dp->list; p != NULL; p = p->fts_link) {
316b94f2f87Sderaadt 		if (IS_NOPRINT(p))
317b94f2f87Sderaadt 			continue;
318b94f2f87Sderaadt 		if (col > 0) {
319b94f2f87Sderaadt 			(void)putchar(','), col++;
320b6203726Sschwarze 			if (col + 1 + extwidth + mbsprint(p->fts_name, 0) >=
321b6203726Sschwarze 			    termwidth)
322b94f2f87Sderaadt 				(void)putchar('\n'), col = 0;
323b94f2f87Sderaadt 			else
324b94f2f87Sderaadt 				(void)putchar(' '), col++;
325b94f2f87Sderaadt 		}
326b94f2f87Sderaadt 		col += printaname(p, dp->s_inode, dp->s_block);
327b94f2f87Sderaadt 	}
328b94f2f87Sderaadt 	(void)putchar('\n');
329b94f2f87Sderaadt }
330b94f2f87Sderaadt 
331df930be7Sderaadt static int
printtype(mode_t mode)3324442fdb7Skrw printtype(mode_t mode)
333df930be7Sderaadt {
334df930be7Sderaadt 	switch (mode & S_IFMT) {
335df930be7Sderaadt 	case S_IFDIR:
336df930be7Sderaadt 		(void)putchar('/');
337df930be7Sderaadt 		return (1);
338df930be7Sderaadt 	case S_IFIFO:
339df930be7Sderaadt 		(void)putchar('|');
340df930be7Sderaadt 		return (1);
341df930be7Sderaadt 	case S_IFLNK:
342df930be7Sderaadt 		(void)putchar('@');
343df930be7Sderaadt 		return (1);
344df930be7Sderaadt 	case S_IFSOCK:
345df930be7Sderaadt 		(void)putchar('=');
346df930be7Sderaadt 		return (1);
347df930be7Sderaadt 	}
348df930be7Sderaadt 	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
349df930be7Sderaadt 		(void)putchar('*');
350df930be7Sderaadt 		return (1);
351df930be7Sderaadt 	}
352df930be7Sderaadt 	return (0);
353df930be7Sderaadt }
354df930be7Sderaadt 
355df930be7Sderaadt static void
printlink(FTSENT * p)35628416801Sderaadt printlink(FTSENT *p)
357df930be7Sderaadt {
358df930be7Sderaadt 	int lnklen;
359b9fc9a72Sderaadt 	char name[PATH_MAX], path[PATH_MAX];
360df930be7Sderaadt 
361df930be7Sderaadt 	if (p->fts_level == FTS_ROOTLEVEL)
362df930be7Sderaadt 		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
363df930be7Sderaadt 	else
364df930be7Sderaadt 		(void)snprintf(name, sizeof(name),
365df930be7Sderaadt 		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
366df930be7Sderaadt 	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
367df930be7Sderaadt 		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
368df930be7Sderaadt 		return;
369df930be7Sderaadt 	}
370df930be7Sderaadt 	path[lnklen] = '\0';
371413925e6Sderaadt 	(void)printf(" -> ");
372b6203726Sschwarze 	(void)mbsprint(path, 1);
373df930be7Sderaadt }
37491ea06e0Stedu 
37591ea06e0Stedu static void
printsize(int width,off_t bytes)3767e8a4e4eSkrw printsize(int width, off_t bytes)
37791ea06e0Stedu {
37891ea06e0Stedu 	char ret[FMT_SCALED_STRSIZE];
37991ea06e0Stedu 
38091ea06e0Stedu 	if ((f_humanval) && (fmt_scaled(bytes, ret) != -1)) {
3817e8a4e4eSkrw 		(void)printf("%*s ", width, ret);
38291ea06e0Stedu 		return;
38391ea06e0Stedu 	}
3847e8a4e4eSkrw 	(void)printf("%*lld ", width, (long long)bytes);
38591ea06e0Stedu }
386