Annotation of src/bin/ls/print.c, Revision 1.57
1.57 ! christos 1: /* $NetBSD: print.c,v 1.56 2020/05/16 18:31:45 christos Exp $ */
1.13 cgd 2:
1.1 cgd 3: /*
1.11 mycroft 4: * Copyright (c) 1989, 1993, 1994
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 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.
1.35 agc 18: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 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:
1.16 christos 35: #include <sys/cdefs.h>
1.1 cgd 36: #ifndef lint
1.13 cgd 37: #if 0
1.14 jtc 38: static char sccsid[] = "@(#)print.c 8.5 (Berkeley) 7/28/94";
1.13 cgd 39: #else
1.57 ! christos 40: __RCSID("$NetBSD: print.c,v 1.56 2020/05/16 18:31:45 christos Exp $");
1.13 cgd 41: #endif
1.1 cgd 42: #endif /* not lint */
43:
44: #include <sys/param.h>
45: #include <sys/stat.h>
1.57 ! christos 46: #ifndef SMALL
1.56 christos 47: #include <sys/acl.h>
1.57 ! christos 48: #endif
1.11 mycroft 49:
50: #include <err.h>
51: #include <errno.h>
1.55 martin 52: #include <inttypes.h>
1.5 mycroft 53: #include <fts.h>
1.1 cgd 54: #include <grp.h>
55: #include <pwd.h>
1.11 mycroft 56: #include <stdio.h>
1.5 mycroft 57: #include <stdlib.h>
58: #include <string.h>
1.11 mycroft 59: #include <time.h>
60: #include <tzfile.h>
61: #include <unistd.h>
1.38 grant 62: #include <util.h>
1.11 mycroft 63:
1.1 cgd 64: #include "ls.h"
1.5 mycroft 65: #include "extern.h"
1.1 cgd 66:
1.31 christos 67: extern int termwidth;
68:
1.30 lukem 69: static int printaname(FTSENT *, int, int);
70: static void printlink(FTSENT *);
71: static void printtime(time_t);
1.43 ahoka 72: static void printtotal(DISPLAY *dp);
1.30 lukem 73: static int printtype(u_int);
1.57 ! christos 74: #ifndef SMALL
1.56 christos 75: static void aclmode(char *, const FTSENT *);
1.57 ! christos 76: #endif
1.5 mycroft 77:
1.20 mycroft 78: static time_t now;
79:
1.5 mycroft 80: #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
81:
1.53 christos 82: static int
83: safe_printpath(const FTSENT *p) {
84: int chcnt;
85:
86: if (f_fullpath) {
87: chcnt = safe_print(p->fts_path);
88: chcnt += safe_print("/");
89: } else
90: chcnt = 0;
91: return chcnt + safe_print(p->fts_name);
92: }
93:
94: static int
95: printescapedpath(const FTSENT *p) {
96: int chcnt;
97:
98: if (f_fullpath) {
99: chcnt = printescaped(p->fts_path);
100: chcnt += printescaped("/");
101: } else
102: chcnt = 0;
103:
104: return chcnt + printescaped(p->fts_name);
105: }
106:
107: static int
108: printpath(const FTSENT *p) {
109: if (f_fullpath)
110: return printf("%s/%s", p->fts_path, p->fts_name);
111: else
1.54 mlelstv 112: return printf("%s", p->fts_name);
1.53 christos 113: }
114:
1.5 mycroft 115: void
1.30 lukem 116: printscol(DISPLAY *dp)
1.1 cgd 117: {
1.11 mycroft 118: FTSENT *p;
1.5 mycroft 119:
120: for (p = dp->list; p; p = p->fts_link) {
121: if (IS_NOPRINT(p))
122: continue;
123: (void)printaname(p, dp->s_inode, dp->s_block);
1.1 cgd 124: (void)putchar('\n');
125: }
126: }
127:
1.5 mycroft 128: void
1.30 lukem 129: printlong(DISPLAY *dp)
1.5 mycroft 130: {
1.11 mycroft 131: struct stat *sp;
132: FTSENT *p;
1.5 mycroft 133: NAMES *np;
1.38 grant 134: char buf[20], szbuf[5];
1.5 mycroft 135:
1.22 mycroft 136: now = time(NULL);
1.20 mycroft 137:
1.53 christos 138: if (!f_leafonly)
139: printtotal(dp); /* "total: %u\n" */
1.43 ahoka 140:
1.5 mycroft 141: for (p = dp->list; p; p = p->fts_link) {
142: if (IS_NOPRINT(p))
143: continue;
144: sp = p->fts_statp;
1.1 cgd 145: if (f_inode)
1.55 martin 146: (void)printf("%*"PRIu64" ", dp->s_inode, sp->st_ino);
1.41 jschauma 147: if (f_size) {
148: if (f_humanize) {
149: if ((humanize_number(szbuf, sizeof(szbuf),
1.48 enami 150: sp->st_blocks * S_BLKSIZE,
151: "", HN_AUTOSCALE,
152: (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
153: err(1, "humanize_number");
154: (void)printf("%*s ", dp->s_block, szbuf);
1.41 jschauma 155: } else {
1.50 christos 156: (void)printf(f_commas ? "%'*llu " : "%*llu ",
157: dp->s_block,
158: (unsigned long long)howmany(sp->st_blocks,
1.48 enami 159: blocksize));
1.41 jschauma 160: }
1.38 grant 161: }
1.5 mycroft 162: (void)strmode(sp->st_mode, buf);
1.57 ! christos 163: #ifndef SMALL
1.56 christos 164: aclmode(buf, p);
1.57 ! christos 165: #endif
1.5 mycroft 166: np = p->fts_pointer;
1.34 grant 167: (void)printf("%s %*lu ", buf, dp->s_nlink,
168: (unsigned long)sp->st_nlink);
169: if (!f_grouponly)
170: (void)printf("%-*s ", dp->s_user, np->user);
171: (void)printf("%-*s ", dp->s_group, np->group);
1.5 mycroft 172: if (f_flags)
173: (void)printf("%-*s ", dp->s_flags, np->flags);
174: if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
1.44 christos 175: (void)printf("%*lld, %*lld ",
176: dp->s_major, (long long)major(sp->st_rdev),
177: dp->s_minor, (long long)minor(sp->st_rdev));
1.1 cgd 178: else
1.38 grant 179: if (f_humanize) {
180: if ((humanize_number(szbuf, sizeof(szbuf),
181: sp->st_size, "", HN_AUTOSCALE,
182: (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
183: err(1, "humanize_number");
184: (void)printf("%*s ", dp->s_size, szbuf);
185: } else {
1.50 christos 186: (void)printf(f_commas ? "%'*llu " : "%*llu ",
187: dp->s_size, (unsigned long long)
188: sp->st_size);
1.38 grant 189: }
1.1 cgd 190: if (f_accesstime)
1.5 mycroft 191: printtime(sp->st_atime);
1.1 cgd 192: else if (f_statustime)
1.5 mycroft 193: printtime(sp->st_ctime);
1.1 cgd 194: else
1.5 mycroft 195: printtime(sp->st_mtime);
1.37 jschauma 196: if (f_octal || f_octal_escape)
1.53 christos 197: (void)safe_printpath(p);
1.36 jschauma 198: else if (f_nonprint)
1.53 christos 199: (void)printescapedpath(p);
1.28 assar 200: else
1.53 christos 201: (void)printpath(p);
1.28 assar 202:
1.26 kleink 203: if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
1.5 mycroft 204: (void)printtype(sp->st_mode);
205: if (S_ISLNK(sp->st_mode))
206: printlink(p);
1.1 cgd 207: (void)putchar('\n');
208: }
209: }
210:
1.5 mycroft 211: void
1.30 lukem 212: printcol(DISPLAY *dp)
1.1 cgd 213: {
1.5 mycroft 214: static FTSENT **array;
215: static int lastentries = -1;
1.11 mycroft 216: FTSENT *p;
1.16 christos 217: int base, chcnt, col, colwidth, num;
1.15 thorpej 218: int numcols, numrows, row;
1.1 cgd 219:
1.19 lukem 220: colwidth = dp->maxlen;
221: if (f_inode)
222: colwidth += dp->s_inode + 1;
1.38 grant 223: if (f_size) {
224: if (f_humanize)
225: colwidth += dp->s_size + 1;
226: else
227: colwidth += dp->s_block + 1;
228: }
1.26 kleink 229: if (f_type || f_typedir)
1.19 lukem 230: colwidth += 1;
231:
232: colwidth += 1;
233:
234: if (termwidth < 2 * colwidth) {
235: printscol(dp);
236: return;
237: }
238:
1.5 mycroft 239: /*
240: * Have to do random access in the linked list -- build a table
241: * of pointers.
242: */
243: if (dp->entries > lastentries) {
1.51 yamt 244: FTSENT **newarray;
245:
246: newarray = realloc(array, dp->entries * sizeof(FTSENT *));
247: if (newarray == NULL) {
1.27 drochner 248: warn(NULL);
1.5 mycroft 249: printscol(dp);
1.51 yamt 250: return;
1.5 mycroft 251: }
1.51 yamt 252: lastentries = dp->entries;
253: array = newarray;
1.5 mycroft 254: }
255: for (p = dp->list, num = 0; p; p = p->fts_link)
256: if (p->fts_number != NO_PRINT)
257: array[num++] = p;
258:
1.19 lukem 259: numcols = termwidth / colwidth;
260: colwidth = termwidth / numcols; /* spread out if possible */
261: numrows = num / numcols;
262: if (num % numcols)
263: ++numrows;
264:
1.43 ahoka 265: printtotal(dp); /* "total: %u\n" */
266:
1.19 lukem 267: for (row = 0; row < numrows; ++row) {
268: for (base = row, chcnt = col = 0; col < numcols; ++col) {
269: chcnt = printaname(array[base], dp->s_inode,
1.38 grant 270: f_humanize ? dp->s_size : dp->s_block);
1.19 lukem 271: if ((base += numrows) >= num)
272: break;
273: while (chcnt++ < colwidth)
1.22 mycroft 274: (void)putchar(' ');
1.19 lukem 275: }
276: (void)putchar('\n');
277: }
278: }
279:
280: void
1.30 lukem 281: printacol(DISPLAY *dp)
1.19 lukem 282: {
283: FTSENT *p;
284: int chcnt, col, colwidth;
285: int numcols;
286:
1.5 mycroft 287: colwidth = dp->maxlen;
1.1 cgd 288: if (f_inode)
1.5 mycroft 289: colwidth += dp->s_inode + 1;
1.38 grant 290: if (f_size) {
291: if (f_humanize)
292: colwidth += dp->s_size + 1;
293: else
294: colwidth += dp->s_block + 1;
295: }
1.26 kleink 296: if (f_type || f_typedir)
1.1 cgd 297: colwidth += 1;
298:
1.15 thorpej 299: colwidth += 1;
300:
1.1 cgd 301: if (termwidth < 2 * colwidth) {
1.5 mycroft 302: printscol(dp);
1.1 cgd 303: return;
304: }
305:
306: numcols = termwidth / colwidth;
1.15 thorpej 307: colwidth = termwidth / numcols; /* spread out if possible */
1.1 cgd 308:
1.43 ahoka 309: printtotal(dp); /* "total: %u\n" */
310:
1.19 lukem 311: chcnt = col = 0;
312: for (p = dp->list; p; p = p->fts_link) {
313: if (IS_NOPRINT(p))
314: continue;
315: if (col >= numcols) {
316: chcnt = col = 0;
1.22 mycroft 317: (void)putchar('\n');
1.1 cgd 318: }
1.38 grant 319: chcnt = printaname(p, dp->s_inode,
320: f_humanize ? dp->s_size : dp->s_block);
1.19 lukem 321: while (chcnt++ < colwidth)
1.22 mycroft 322: (void)putchar(' ');
1.19 lukem 323: col++;
1.25 kleink 324: }
325: (void)putchar('\n');
326: }
327:
328: void
1.30 lukem 329: printstream(DISPLAY *dp)
1.25 kleink 330: {
331: FTSENT *p;
332: int col;
333: int extwidth;
334:
335: extwidth = 0;
336: if (f_inode)
337: extwidth += dp->s_inode + 1;
1.38 grant 338: if (f_size) {
339: if (f_humanize)
340: extwidth += dp->s_size + 1;
341: else
342: extwidth += dp->s_block + 1;
343: }
1.25 kleink 344: if (f_type)
345: extwidth += 1;
346:
347: for (col = 0, p = dp->list; p != NULL; p = p->fts_link) {
348: if (IS_NOPRINT(p))
349: continue;
350: if (col > 0) {
351: (void)putchar(','), col++;
1.45 lukem 352: if (col + 1 + extwidth + (int)p->fts_namelen >= termwidth)
1.25 kleink 353: (void)putchar('\n'), col = 0;
354: else
355: (void)putchar(' '), col++;
356: }
1.38 grant 357: col += printaname(p, dp->s_inode,
358: f_humanize ? dp->s_size : dp->s_block);
1.1 cgd 359: }
1.22 mycroft 360: (void)putchar('\n');
1.1 cgd 361: }
362:
363: /*
364: * print [inode] [size] name
1.5 mycroft 365: * return # of characters printed, no trailing characters.
1.1 cgd 366: */
1.5 mycroft 367: static int
1.30 lukem 368: printaname(FTSENT *p, int inodefield, int sizefield)
1.1 cgd 369: {
1.5 mycroft 370: struct stat *sp;
1.1 cgd 371: int chcnt;
1.38 grant 372: char szbuf[5];
1.1 cgd 373:
1.5 mycroft 374: sp = p->fts_statp;
1.1 cgd 375: chcnt = 0;
376: if (f_inode)
1.55 martin 377: chcnt += printf("%*"PRIu64" ", inodefield, sp->st_ino);
1.38 grant 378: if (f_size) {
379: if (f_humanize) {
380: if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size,
381: "", HN_AUTOSCALE,
382: (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
383: err(1, "humanize_number");
384: chcnt += printf("%*s ", sizefield, szbuf);
385: } else {
1.50 christos 386: chcnt += printf(f_commas ? "%'*llu " : "%*llu ",
387: sizefield, (unsigned long long)
388: howmany(sp->st_blocks, blocksize));
1.38 grant 389: }
390: }
1.37 jschauma 391: if (f_octal || f_octal_escape)
1.53 christos 392: chcnt += safe_printpath(p);
1.36 jschauma 393: else if (f_nonprint)
1.53 christos 394: chcnt += printescapedpath(p);
1.29 assar 395: else
1.53 christos 396: chcnt += printpath(p);
1.26 kleink 397: if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
1.5 mycroft 398: chcnt += printtype(sp->st_mode);
399: return (chcnt);
1.1 cgd 400: }
401:
1.5 mycroft 402: static void
1.30 lukem 403: printtime(time_t ftime)
1.1 cgd 404: {
405: int i;
1.46 christos 406: const char *longstring;
1.1 cgd 407:
1.47 christos 408: if ((longstring = ctime(&ftime)) == NULL) {
1.46 christos 409: /* 012345678901234567890123 */
410: longstring = "????????????????????????";
411: }
1.1 cgd 412: for (i = 4; i < 11; ++i)
413: (void)putchar(longstring[i]);
414:
415: #define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY)
416: if (f_sectime)
417: for (i = 11; i < 24; i++)
418: (void)putchar(longstring[i]);
1.40 mycroft 419: else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now)
1.1 cgd 420: for (i = 11; i < 16; ++i)
421: (void)putchar(longstring[i]);
422: else {
423: (void)putchar(' ');
424: for (i = 20; i < 24; ++i)
425: (void)putchar(longstring[i]);
426: }
427: (void)putchar(' ');
428: }
429:
1.43 ahoka 430: /*
431: * Display total used disk space in the form "total: %u\n".
432: * Note: POSIX (IEEE Std 1003.1-2001) says this should be always in 512 blocks,
1.49 erh 433: * but we humanise it with -h, or separate it with commas with -M, and use 1024
434: * with -k.
1.43 ahoka 435: */
436: static void
437: printtotal(DISPLAY *dp)
438: {
439: char szbuf[5];
440:
441: if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) {
442: if (f_humanize) {
443: if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal,
444: "", HN_AUTOSCALE,
445: (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
446: err(1, "humanize_number");
447: (void)printf("total %s\n", szbuf);
448: } else {
1.50 christos 449: (void)printf(f_commas ? "total %'llu\n" :
450: "total %llu\n", (unsigned long long)
451: howmany(dp->btotal, blocksize));
1.43 ahoka 452: }
453: }
454: }
455:
1.5 mycroft 456: static int
1.30 lukem 457: printtype(u_int mode)
1.1 cgd 458: {
1.11 mycroft 459: switch (mode & S_IFMT) {
1.1 cgd 460: case S_IFDIR:
461: (void)putchar('/');
1.5 mycroft 462: return (1);
1.11 mycroft 463: case S_IFIFO:
464: (void)putchar('|');
465: return (1);
1.1 cgd 466: case S_IFLNK:
467: (void)putchar('@');
1.5 mycroft 468: return (1);
1.1 cgd 469: case S_IFSOCK:
470: (void)putchar('=');
1.12 mycroft 471: return (1);
472: case S_IFWHT:
473: (void)putchar('%');
1.6 jtc 474: return (1);
1.1 cgd 475: }
476: if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
477: (void)putchar('*');
1.5 mycroft 478: return (1);
1.1 cgd 479: }
1.5 mycroft 480: return (0);
1.1 cgd 481: }
482:
1.5 mycroft 483: static void
1.30 lukem 484: printlink(FTSENT *p)
1.1 cgd 485: {
486: int lnklen;
1.5 mycroft 487: char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1];
488:
489: if (p->fts_level == FTS_ROOTLEVEL)
490: (void)snprintf(name, sizeof(name), "%s", p->fts_name);
1.33 enami 491: else
1.11 mycroft 492: (void)snprintf(name, sizeof(name),
493: "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
494: if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
1.1 cgd 495: (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
496: return;
497: }
498: path[lnklen] = '\0';
1.28 assar 499: (void)printf(" -> ");
1.37 jschauma 500: if (f_octal || f_octal_escape)
1.36 jschauma 501: (void)safe_print(path);
502: else if (f_nonprint)
503: (void)printescaped(path);
1.28 assar 504: else
505: (void)printf("%s", path);
1.1 cgd 506: }
1.56 christos 507:
1.57 ! christos 508: #ifndef SMALL
1.56 christos 509: /*
510: * Add a + after the standard rwxrwxrwx mode if the file has an
511: * ACL. strmode() reserves space at the end of the string.
512: */
513: static void
514: aclmode(char *buf, const FTSENT *p)
515: {
516: char name[MAXPATHLEN + 1];
517: int ret, trivial;
518: static dev_t previous_dev = NODEV;
519: static int supports_acls = -1;
520: static int type = ACL_TYPE_ACCESS;
521: acl_t facl;
522:
523: /*
524: * XXX: ACLs are not supported on whiteouts and device files
525: * residing on UFS.
526: */
527: if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
528: S_ISWHT(p->fts_statp->st_mode))
529: return;
530:
531: if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
532: return;
533:
534: if (p->fts_level == FTS_ROOTLEVEL)
535: snprintf(name, sizeof(name), "%s", p->fts_name);
536: else
537: snprintf(name, sizeof(name), "%s/%s",
538: p->fts_parent->fts_accpath, p->fts_name);
539:
540: if (supports_acls == -1 || previous_dev != p->fts_statp->st_dev) {
541: previous_dev = p->fts_statp->st_dev;
542: supports_acls = 0;
543:
544: ret = lpathconf(name, _PC_ACL_NFS4);
545: if (ret > 0) {
546: type = ACL_TYPE_NFS4;
547: supports_acls = 1;
548: } else if (ret < 0 && errno != EINVAL) {
549: warn("%s", name);
550: return;
551: }
552: if (supports_acls == 0) {
553: ret = lpathconf(name, _PC_ACL_EXTENDED);
554: if (ret > 0) {
555: type = ACL_TYPE_ACCESS;
556: supports_acls = 1;
557: } else if (ret < 0 && errno != EINVAL) {
558: warn("%s", name);
559: return;
560: }
561: }
562: }
563: if (supports_acls == 0)
564: return;
565: facl = acl_get_link_np(name, type);
566: if (facl == NULL) {
567: warn("%s", name);
568: return;
569: }
570: if (acl_is_trivial_np(facl, &trivial)) {
571: acl_free(facl);
572: warn("%s", name);
573: return;
574: }
575: if (!trivial)
576: buf[10] = '+';
577: acl_free(facl);
578: }
1.57 ! christos 579: #endif
CVSweb <webmaster@jp.NetBSD.org>