1/*	$NetBSD: print.c,v 1.50 2011/03/15 22:53:41 christos Exp $	*/
2
3/*
4 * Copyright (c) 1989, 1993, 1994
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Michael Fischbein.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)print.c	8.5 (Berkeley) 7/28/94";
39#else
40__RCSID("$NetBSD: print.c,v 1.50 2011/03/15 22:53:41 christos Exp $");
41#endif
42#endif /* not lint */
43
44#include <sys/param.h>
45#include <sys/stat.h>
46
47#include <err.h>
48#include <errno.h>
49#include <fts.h>
50#include <grp.h>
51#include <pwd.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <time.h>
56#include <tzfile.h>
57#include <unistd.h>
58#include <util.h>
59
60#include "ls.h"
61#include "extern.h"
62
63extern int termwidth;
64
65static int	printaname(FTSENT *, int, int);
66static void	printlink(FTSENT *);
67static void	printtime(time_t);
68static void	printtotal(DISPLAY *dp);
69static int	printtype(u_int);
70
71static time_t	now;
72
73#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
74
75void
76printscol(DISPLAY *dp)
77{
78	FTSENT *p;
79
80	for (p = dp->list; p; p = p->fts_link) {
81		if (IS_NOPRINT(p))
82			continue;
83		(void)printaname(p, dp->s_inode, dp->s_block);
84		(void)putchar('\n');
85	}
86}
87
88void
89printlong(DISPLAY *dp)
90{
91	struct stat *sp;
92	FTSENT *p;
93	NAMES *np;
94	char buf[20], szbuf[5];
95
96	now = time(NULL);
97
98	printtotal(dp);		/* "total: %u\n" */
99
100	for (p = dp->list; p; p = p->fts_link) {
101		if (IS_NOPRINT(p))
102			continue;
103		sp = p->fts_statp;
104		if (f_inode)
105			(void)printf("%*lu ", dp->s_inode,
106			    (unsigned long)sp->st_ino);
107		if (f_size) {
108			if (f_humanize) {
109				if ((humanize_number(szbuf, sizeof(szbuf),
110				    sp->st_blocks * S_BLKSIZE,
111				    "", HN_AUTOSCALE,
112				    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
113					err(1, "humanize_number");
114				(void)printf("%*s ", dp->s_block, szbuf);
115			} else {
116				(void)printf(f_commas ? "%'*llu " : "%*llu ",
117				    dp->s_block,
118				    (unsigned long long)howmany(sp->st_blocks,
119				    blocksize));
120			}
121		}
122		(void)strmode(sp->st_mode, buf);
123		np = p->fts_pointer;
124		(void)printf("%s %*lu ", buf, dp->s_nlink,
125		    (unsigned long)sp->st_nlink);
126		if (!f_grouponly)
127			(void)printf("%-*s  ", dp->s_user, np->user);
128		(void)printf("%-*s  ", dp->s_group, np->group);
129		if (f_flags)
130			(void)printf("%-*s ", dp->s_flags, np->flags);
131		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
132			(void)printf("%*lld, %*lld ",
133			    dp->s_major, (long long)major(sp->st_rdev),
134			    dp->s_minor, (long long)minor(sp->st_rdev));
135		else
136			if (f_humanize) {
137				if ((humanize_number(szbuf, sizeof(szbuf),
138				    sp->st_size, "", HN_AUTOSCALE,
139				    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
140					err(1, "humanize_number");
141				(void)printf("%*s ", dp->s_size, szbuf);
142			} else {
143				(void)printf(f_commas ? "%'*llu " : "%*llu ",
144				    dp->s_size, (unsigned long long)
145				    sp->st_size);
146			}
147		if (f_accesstime)
148			printtime(sp->st_atime);
149		else if (f_statustime)
150			printtime(sp->st_ctime);
151		else
152			printtime(sp->st_mtime);
153		if (f_octal || f_octal_escape)
154			(void)safe_print(p->fts_name);
155		else if (f_nonprint)
156			(void)printescaped(p->fts_name);
157		else
158			(void)printf("%s", p->fts_name);
159
160		if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
161			(void)printtype(sp->st_mode);
162		if (S_ISLNK(sp->st_mode))
163			printlink(p);
164		(void)putchar('\n');
165	}
166}
167
168void
169printcol(DISPLAY *dp)
170{
171	static FTSENT **array;
172	static int lastentries = -1;
173	FTSENT *p;
174	int base, chcnt, col, colwidth, num;
175	int numcols, numrows, row;
176
177	colwidth = dp->maxlen;
178	if (f_inode)
179		colwidth += dp->s_inode + 1;
180	if (f_size) {
181		if (f_humanize)
182			colwidth += dp->s_size + 1;
183		else
184			colwidth += dp->s_block + 1;
185	}
186	if (f_type || f_typedir)
187		colwidth += 1;
188
189	colwidth += 1;
190
191	if (termwidth < 2 * colwidth) {
192		printscol(dp);
193		return;
194	}
195
196	/*
197	 * Have to do random access in the linked list -- build a table
198	 * of pointers.
199	 */
200	if (dp->entries > lastentries) {
201		lastentries = dp->entries;
202		if ((array =
203		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
204			warn(NULL);
205			printscol(dp);
206		}
207	}
208	for (p = dp->list, num = 0; p; p = p->fts_link)
209		if (p->fts_number != NO_PRINT)
210			array[num++] = p;
211
212	numcols = termwidth / colwidth;
213	colwidth = termwidth / numcols;		/* spread out if possible */
214	numrows = num / numcols;
215	if (num % numcols)
216		++numrows;
217
218	printtotal(dp);				/* "total: %u\n" */
219
220	for (row = 0; row < numrows; ++row) {
221		for (base = row, chcnt = col = 0; col < numcols; ++col) {
222			chcnt = printaname(array[base], dp->s_inode,
223			    f_humanize ? dp->s_size : dp->s_block);
224			if ((base += numrows) >= num)
225				break;
226			while (chcnt++ < colwidth)
227				(void)putchar(' ');
228		}
229		(void)putchar('\n');
230	}
231}
232
233void
234printacol(DISPLAY *dp)
235{
236	FTSENT *p;
237	int chcnt, col, colwidth;
238	int numcols;
239
240	colwidth = dp->maxlen;
241	if (f_inode)
242		colwidth += dp->s_inode + 1;
243	if (f_size) {
244		if (f_humanize)
245			colwidth += dp->s_size + 1;
246		else
247			colwidth += dp->s_block + 1;
248	}
249	if (f_type || f_typedir)
250		colwidth += 1;
251
252	colwidth += 1;
253
254	if (termwidth < 2 * colwidth) {
255		printscol(dp);
256		return;
257	}
258
259	numcols = termwidth / colwidth;
260	colwidth = termwidth / numcols;		/* spread out if possible */
261
262	printtotal(dp);				/* "total: %u\n" */
263
264	chcnt = col = 0;
265	for (p = dp->list; p; p = p->fts_link) {
266		if (IS_NOPRINT(p))
267			continue;
268		if (col >= numcols) {
269			chcnt = col = 0;
270			(void)putchar('\n');
271		}
272		chcnt = printaname(p, dp->s_inode,
273		    f_humanize ? dp->s_size : dp->s_block);
274		while (chcnt++ < colwidth)
275			(void)putchar(' ');
276		col++;
277	}
278	(void)putchar('\n');
279}
280
281void
282printstream(DISPLAY *dp)
283{
284	FTSENT *p;
285	int col;
286	int extwidth;
287
288	extwidth = 0;
289	if (f_inode)
290		extwidth += dp->s_inode + 1;
291	if (f_size) {
292		if (f_humanize)
293			extwidth += dp->s_size + 1;
294		else
295			extwidth += dp->s_block + 1;
296	}
297	if (f_type)
298		extwidth += 1;
299
300	for (col = 0, p = dp->list; p != NULL; p = p->fts_link) {
301		if (IS_NOPRINT(p))
302			continue;
303		if (col > 0) {
304			(void)putchar(','), col++;
305			if (col + 1 + extwidth + (int)p->fts_namelen >= termwidth)
306				(void)putchar('\n'), col = 0;
307			else
308				(void)putchar(' '), col++;
309		}
310		col += printaname(p, dp->s_inode,
311		    f_humanize ? dp->s_size : dp->s_block);
312	}
313	(void)putchar('\n');
314}
315
316/*
317 * print [inode] [size] name
318 * return # of characters printed, no trailing characters.
319 */
320static int
321printaname(FTSENT *p, int inodefield, int sizefield)
322{
323	struct stat *sp;
324	int chcnt;
325	char szbuf[5];
326
327	sp = p->fts_statp;
328	chcnt = 0;
329	if (f_inode)
330		chcnt += printf("%*lu ", inodefield, (unsigned long)sp->st_ino);
331	if (f_size) {
332		if (f_humanize) {
333			if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size,
334			    "", HN_AUTOSCALE,
335			    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
336				err(1, "humanize_number");
337			chcnt += printf("%*s ", sizefield, szbuf);
338		} else {
339			chcnt += printf(f_commas ? "%'*llu " : "%*llu ",
340			    sizefield, (unsigned long long)
341			    howmany(sp->st_blocks, blocksize));
342		}
343	}
344	if (f_octal || f_octal_escape)
345		chcnt += safe_print(p->fts_name);
346	else if (f_nonprint)
347		chcnt += printescaped(p->fts_name);
348	else
349		chcnt += printf("%s", p->fts_name);
350	if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
351		chcnt += printtype(sp->st_mode);
352	return (chcnt);
353}
354
355static void
356printtime(time_t ftime)
357{
358	int i;
359	const char *longstring;
360
361	if ((longstring = ctime(&ftime)) == NULL) {
362			   /* 012345678901234567890123 */
363		longstring = "????????????????????????";
364	}
365	for (i = 4; i < 11; ++i)
366		(void)putchar(longstring[i]);
367
368#define	SIXMONTHS	((DAYSPERNYEAR / 2) * SECSPERDAY)
369	if (f_sectime)
370		for (i = 11; i < 24; i++)
371			(void)putchar(longstring[i]);
372	else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now)
373		for (i = 11; i < 16; ++i)
374			(void)putchar(longstring[i]);
375	else {
376		(void)putchar(' ');
377		for (i = 20; i < 24; ++i)
378			(void)putchar(longstring[i]);
379	}
380	(void)putchar(' ');
381}
382
383/*
384 * Display total used disk space in the form "total: %u\n".
385 * Note: POSIX (IEEE Std 1003.1-2001) says this should be always in 512 blocks,
386 * but we humanise it with -h, or separate it with commas with -M, and use 1024
387 * with -k.
388 */
389static void
390printtotal(DISPLAY *dp)
391{
392	char szbuf[5];
393
394	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) {
395		if (f_humanize) {
396			if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal,
397			    "", HN_AUTOSCALE,
398			    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
399				err(1, "humanize_number");
400			(void)printf("total %s\n", szbuf);
401		} else {
402			(void)printf(f_commas ? "total %'llu\n" :
403			    "total %llu\n", (unsigned long long)
404			    howmany(dp->btotal, blocksize));
405		}
406	}
407}
408
409static int
410printtype(u_int mode)
411{
412	switch (mode & S_IFMT) {
413	case S_IFDIR:
414		(void)putchar('/');
415		return (1);
416	case S_IFIFO:
417		(void)putchar('|');
418		return (1);
419	case S_IFLNK:
420		(void)putchar('@');
421		return (1);
422	case S_IFSOCK:
423		(void)putchar('=');
424		return (1);
425	case S_IFWHT:
426		(void)putchar('%');
427		return (1);
428	}
429	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
430		(void)putchar('*');
431		return (1);
432	}
433	return (0);
434}
435
436static void
437printlink(FTSENT *p)
438{
439	int lnklen;
440	char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1];
441
442	if (p->fts_level == FTS_ROOTLEVEL)
443		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
444	else
445		(void)snprintf(name, sizeof(name),
446		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
447	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
448		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
449		return;
450	}
451	path[lnklen] = '\0';
452	(void)printf(" -> ");
453	if (f_octal || f_octal_escape)
454		(void)safe_print(path);
455	else if (f_nonprint)
456		(void)printescaped(path);
457	else
458		(void)printf("%s", path);
459}
460
461