xref: /netbsd-src/bin/ls/print.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: print.c,v 1.41 2005/10/31 14:13:33 jschauma 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
38 static char sccsid[] = "@(#)print.c	8.5 (Berkeley) 7/28/94";
39 #else
40 __RCSID("$NetBSD: print.c,v 1.41 2005/10/31 14:13:33 jschauma 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 
63 extern int termwidth;
64 
65 static int	printaname(FTSENT *, int, int);
66 static void	printlink(FTSENT *);
67 static void	printtime(time_t);
68 static int	printtype(u_int);
69 
70 static time_t	now;
71 
72 #define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
73 
74 void
75 printscol(DISPLAY *dp)
76 {
77 	FTSENT *p;
78 
79 	for (p = dp->list; p; p = p->fts_link) {
80 		if (IS_NOPRINT(p))
81 			continue;
82 		(void)printaname(p, dp->s_inode, dp->s_block);
83 		(void)putchar('\n');
84 	}
85 }
86 
87 void
88 printlong(DISPLAY *dp)
89 {
90 	struct stat *sp;
91 	FTSENT *p;
92 	NAMES *np;
93 	char buf[20], szbuf[5];
94 
95 	now = time(NULL);
96 
97 	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) {
98 		if (f_humanize) {
99 			if ((humanize_number(szbuf, sizeof(szbuf), dp->stotal,
100 			    "", HN_AUTOSCALE,
101 			    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
102 				err(1, "humanize_number");
103 			(void)printf("total %s\n", szbuf);
104 		} else {
105 			(void)printf("total %llu\n",
106 			    (long long)(howmany(dp->btotal, blocksize)));
107 		}
108 	}
109 
110 	for (p = dp->list; p; p = p->fts_link) {
111 		if (IS_NOPRINT(p))
112 			continue;
113 		sp = p->fts_statp;
114 		if (f_inode)
115 			(void)printf("%*lu ", dp->s_inode,
116 			    (unsigned long)sp->st_ino);
117 		if (f_size) {
118 			if (f_humanize) {
119 				if ((humanize_number(szbuf, sizeof(szbuf),
120 					sp->st_blocks * S_BLKSIZE,
121 			    "", HN_AUTOSCALE,
122 			    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
123 				err(1, "humanize_number");
124 			(void)printf("%*s ", dp->s_block, szbuf);
125 			} else {
126 			(void)printf("%*llu ", dp->s_block,
127 					(long long)howmany(sp->st_blocks,
128 							   	blocksize));
129 			}
130 		}
131 		(void)strmode(sp->st_mode, buf);
132 		np = p->fts_pointer;
133 		(void)printf("%s %*lu ", buf, dp->s_nlink,
134 		    (unsigned long)sp->st_nlink);
135 		if (!f_grouponly)
136 			(void)printf("%-*s  ", dp->s_user, np->user);
137 		(void)printf("%-*s  ", dp->s_group, np->group);
138 		if (f_flags)
139 			(void)printf("%-*s ", dp->s_flags, np->flags);
140 		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
141 			(void)printf("%*u, %*u ",
142 			    dp->s_major, major(sp->st_rdev), dp->s_minor,
143 			    minor(sp->st_rdev));
144 		else
145 			if (f_humanize) {
146 				if ((humanize_number(szbuf, sizeof(szbuf),
147 				    sp->st_size, "", HN_AUTOSCALE,
148 				    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
149 					err(1, "humanize_number");
150 				(void)printf("%*s ", dp->s_size, szbuf);
151 			} else {
152 				(void)printf("%*llu ", dp->s_size,
153 				    (long long)sp->st_size);
154 			}
155 		if (f_accesstime)
156 			printtime(sp->st_atime);
157 		else if (f_statustime)
158 			printtime(sp->st_ctime);
159 		else
160 			printtime(sp->st_mtime);
161 		if (f_octal || f_octal_escape)
162 			(void)safe_print(p->fts_name);
163 		else if (f_nonprint)
164 			(void)printescaped(p->fts_name);
165 		else
166 			(void)printf("%s", p->fts_name);
167 
168 		if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
169 			(void)printtype(sp->st_mode);
170 		if (S_ISLNK(sp->st_mode))
171 			printlink(p);
172 		(void)putchar('\n');
173 	}
174 }
175 
176 void
177 printcol(DISPLAY *dp)
178 {
179 	static FTSENT **array;
180 	static int lastentries = -1;
181 	FTSENT *p;
182 	int base, chcnt, col, colwidth, num;
183 	int numcols, numrows, row;
184 	char szbuf[5];
185 
186 	colwidth = dp->maxlen;
187 	if (f_inode)
188 		colwidth += dp->s_inode + 1;
189 	if (f_size) {
190 		if (f_humanize)
191 			colwidth += dp->s_size + 1;
192 		else
193 			colwidth += dp->s_block + 1;
194 	}
195 	if (f_type || f_typedir)
196 		colwidth += 1;
197 
198 	colwidth += 1;
199 
200 	if (termwidth < 2 * colwidth) {
201 		printscol(dp);
202 		return;
203 	}
204 
205 	/*
206 	 * Have to do random access in the linked list -- build a table
207 	 * of pointers.
208 	 */
209 	if (dp->entries > lastentries) {
210 		lastentries = dp->entries;
211 		if ((array =
212 		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
213 			warn(NULL);
214 			printscol(dp);
215 		}
216 	}
217 	for (p = dp->list, num = 0; p; p = p->fts_link)
218 		if (p->fts_number != NO_PRINT)
219 			array[num++] = p;
220 
221 	numcols = termwidth / colwidth;
222 	colwidth = termwidth / numcols;		/* spread out if possible */
223 	numrows = num / numcols;
224 	if (num % numcols)
225 		++numrows;
226 
227 	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) {
228 		if (f_humanize) {
229 			if ((humanize_number(szbuf, sizeof(szbuf), dp->stotal,
230 			    "", HN_AUTOSCALE,
231 			    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
232 				err(1, "humanize_number");
233 			(void)printf("total %s\n", szbuf);
234 		} else {
235 			(void)printf("total %llu\n",
236 			    (long long)(howmany(dp->btotal, blocksize)));
237 		}
238 	}
239 	for (row = 0; row < numrows; ++row) {
240 		for (base = row, chcnt = col = 0; col < numcols; ++col) {
241 			chcnt = printaname(array[base], dp->s_inode,
242 			    f_humanize ? dp->s_size : dp->s_block);
243 			if ((base += numrows) >= num)
244 				break;
245 			while (chcnt++ < colwidth)
246 				(void)putchar(' ');
247 		}
248 		(void)putchar('\n');
249 	}
250 }
251 
252 void
253 printacol(DISPLAY *dp)
254 {
255 	FTSENT *p;
256 	int chcnt, col, colwidth;
257 	int numcols;
258 	char szbuf[5];
259 
260 	colwidth = dp->maxlen;
261 	if (f_inode)
262 		colwidth += dp->s_inode + 1;
263 	if (f_size) {
264 		if (f_humanize)
265 			colwidth += dp->s_size + 1;
266 		else
267 			colwidth += dp->s_block + 1;
268 	}
269 	if (f_type || f_typedir)
270 		colwidth += 1;
271 
272 	colwidth += 1;
273 
274 	if (termwidth < 2 * colwidth) {
275 		printscol(dp);
276 		return;
277 	}
278 
279 	numcols = termwidth / colwidth;
280 	colwidth = termwidth / numcols;		/* spread out if possible */
281 
282 	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) {
283 		if (f_humanize) {
284 			if ((humanize_number(szbuf, sizeof(szbuf), dp->stotal,
285 			    "", HN_AUTOSCALE,
286 			    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
287 				err(1, "humanize_number");
288 			(void)printf("total %s\n", szbuf);
289 		} else {
290 			(void)printf("total %llu\n",
291 			    (long long)(howmany(dp->btotal, blocksize)));
292 		}
293 	}
294 	chcnt = col = 0;
295 	for (p = dp->list; p; p = p->fts_link) {
296 		if (IS_NOPRINT(p))
297 			continue;
298 		if (col >= numcols) {
299 			chcnt = col = 0;
300 			(void)putchar('\n');
301 		}
302 		chcnt = printaname(p, dp->s_inode,
303 		    f_humanize ? dp->s_size : dp->s_block);
304 		while (chcnt++ < colwidth)
305 			(void)putchar(' ');
306 		col++;
307 	}
308 	(void)putchar('\n');
309 }
310 
311 void
312 printstream(DISPLAY *dp)
313 {
314 	FTSENT *p;
315 	int col;
316 	int extwidth;
317 
318 	extwidth = 0;
319 	if (f_inode)
320 		extwidth += dp->s_inode + 1;
321 	if (f_size) {
322 		if (f_humanize)
323 			extwidth += dp->s_size + 1;
324 		else
325 			extwidth += dp->s_block + 1;
326 	}
327 	if (f_type)
328 		extwidth += 1;
329 
330 	for (col = 0, p = dp->list; p != NULL; p = p->fts_link) {
331 		if (IS_NOPRINT(p))
332 			continue;
333 		if (col > 0) {
334 			(void)putchar(','), col++;
335 			if (col + 1 + extwidth + p->fts_namelen >= termwidth)
336 				(void)putchar('\n'), col = 0;
337 			else
338 				(void)putchar(' '), col++;
339 		}
340 		col += printaname(p, dp->s_inode,
341 		    f_humanize ? dp->s_size : dp->s_block);
342 	}
343 	(void)putchar('\n');
344 }
345 
346 /*
347  * print [inode] [size] name
348  * return # of characters printed, no trailing characters.
349  */
350 static int
351 printaname(FTSENT *p, int inodefield, int sizefield)
352 {
353 	struct stat *sp;
354 	int chcnt;
355 	char szbuf[5];
356 
357 	sp = p->fts_statp;
358 	chcnt = 0;
359 	if (f_inode)
360 		chcnt += printf("%*lu ", inodefield, (unsigned long)sp->st_ino);
361 	if (f_size) {
362 		if (f_humanize) {
363 			if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size,
364 			    "", HN_AUTOSCALE,
365 			    (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
366 				err(1, "humanize_number");
367 			chcnt += printf("%*s ", sizefield, szbuf);
368 		} else {
369 			chcnt += printf("%*llu ", sizefield,
370 			    (long long)howmany(sp->st_blocks, blocksize));
371 		}
372 	}
373 	if (f_octal || f_octal_escape)
374 		chcnt += safe_print(p->fts_name);
375 	else if (f_nonprint)
376 		chcnt += printescaped(p->fts_name);
377 	else
378 		chcnt += printf("%s", p->fts_name);
379 	if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
380 		chcnt += printtype(sp->st_mode);
381 	return (chcnt);
382 }
383 
384 static void
385 printtime(time_t ftime)
386 {
387 	int i;
388 	char *longstring;
389 
390 	longstring = ctime(&ftime);
391 	for (i = 4; i < 11; ++i)
392 		(void)putchar(longstring[i]);
393 
394 #define	SIXMONTHS	((DAYSPERNYEAR / 2) * SECSPERDAY)
395 	if (f_sectime)
396 		for (i = 11; i < 24; i++)
397 			(void)putchar(longstring[i]);
398 	else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now)
399 		for (i = 11; i < 16; ++i)
400 			(void)putchar(longstring[i]);
401 	else {
402 		(void)putchar(' ');
403 		for (i = 20; i < 24; ++i)
404 			(void)putchar(longstring[i]);
405 	}
406 	(void)putchar(' ');
407 }
408 
409 static int
410 printtype(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 
436 static void
437 printlink(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