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