1 /*-
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Michael Fischbein.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #if 0
34 #ifndef lint
35 static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94";
36 #endif /* not lint */
37 #endif
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 #include <sys/param.h>
42 #include <sys/stat.h>
43 #include <sys/acl.h>
44
45 #include <err.h>
46 #include <errno.h>
47 #include <fts.h>
48 #include <langinfo.h>
49 #include <libutil.h>
50 #include <limits.h>
51 #include <stdio.h>
52 #include <stdint.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <time.h>
56 #include <unistd.h>
57 #include <wchar.h>
58 #ifdef COLORLS
59 #include <ctype.h>
60 #include <termcap.h>
61 #include <signal.h>
62 #endif
63 #include <libxo/xo.h>
64
65 #include "ls.h"
66 #include "extern.h"
67
68 static int printaname(const FTSENT *, u_long, u_long);
69 static void printdev(size_t, dev_t);
70 static void printlink(const FTSENT *);
71 static void printtime(const char *, time_t);
72 static int printtype(u_int);
73 static void printsize(const char *, size_t, off_t);
74 #ifdef COLORLS
75 static void endcolor(int);
76 static int colortype(mode_t);
77 #endif
78 static void aclmode(char *, const FTSENT *);
79
80 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
81
82 #ifdef COLORLS
83 /* Most of these are taken from <sys/stat.h> */
84 typedef enum Colors {
85 C_DIR, /* directory */
86 C_LNK, /* symbolic link */
87 C_SOCK, /* socket */
88 C_FIFO, /* pipe */
89 C_EXEC, /* executable */
90 C_BLK, /* block special */
91 C_CHR, /* character special */
92 C_SUID, /* setuid executable */
93 C_SGID, /* setgid executable */
94 C_WSDIR, /* directory writeble to others, with sticky
95 * bit */
96 C_WDIR, /* directory writeble to others, without
97 * sticky bit */
98 C_NUMCOLORS /* just a place-holder */
99 } Colors;
100
101 static const char *defcolors = "exfxcxdxbxegedabagacad";
102
103 /* colors for file types */
104 static struct {
105 int num[2];
106 int bold;
107 } colors[C_NUMCOLORS];
108 #endif
109
110 static size_t padding_for_month[12];
111 static size_t month_max_size = 0;
112
113 void
printscol(const DISPLAY * dp)114 printscol(const DISPLAY *dp)
115 {
116 FTSENT *p;
117
118 xo_open_list("entry");
119 for (p = dp->list; p; p = p->fts_link) {
120 if (IS_NOPRINT(p))
121 continue;
122 xo_open_instance("entry");
123 (void)printaname(p, dp->s_inode, dp->s_block);
124 xo_close_instance("entry");
125 xo_emit("\n");
126 }
127 xo_close_list("entry");
128 }
129
130 /*
131 * print name in current style
132 */
133 int
printname(const char * field,const char * name)134 printname(const char *field, const char *name)
135 {
136 char fmt[BUFSIZ];
137 char *s = getname(name);
138 int rc;
139
140 snprintf(fmt, sizeof(fmt), "{:%s/%%hs}", field);
141 rc = xo_emit(fmt, s);
142 free(s);
143 return rc;
144 }
145
146 static const char *
get_abmon(int mon)147 get_abmon(int mon)
148 {
149
150 switch (mon) {
151 case 0: return (nl_langinfo(ABMON_1));
152 case 1: return (nl_langinfo(ABMON_2));
153 case 2: return (nl_langinfo(ABMON_3));
154 case 3: return (nl_langinfo(ABMON_4));
155 case 4: return (nl_langinfo(ABMON_5));
156 case 5: return (nl_langinfo(ABMON_6));
157 case 6: return (nl_langinfo(ABMON_7));
158 case 7: return (nl_langinfo(ABMON_8));
159 case 8: return (nl_langinfo(ABMON_9));
160 case 9: return (nl_langinfo(ABMON_10));
161 case 10: return (nl_langinfo(ABMON_11));
162 case 11: return (nl_langinfo(ABMON_12));
163 }
164
165 /* should never happen */
166 abort();
167 }
168
169 static size_t
mbswidth(const char * month)170 mbswidth(const char *month)
171 {
172 wchar_t wc;
173 size_t width, donelen, clen, w;
174
175 width = donelen = 0;
176 while ((clen = mbrtowc(&wc, month + donelen, MB_LEN_MAX, NULL)) != 0) {
177 if (clen == (size_t)-1 || clen == (size_t)-2)
178 return (-1);
179 donelen += clen;
180 if ((w = wcwidth(wc)) == (size_t)-1)
181 return (-1);
182 width += w;
183 }
184
185 return (width);
186 }
187
188 static void
compute_abbreviated_month_size(void)189 compute_abbreviated_month_size(void)
190 {
191 int i;
192 size_t width;
193 size_t months_width[12];
194
195 for (i = 0; i < 12; i++) {
196 width = mbswidth(get_abmon(i));
197 if (width == (size_t)-1) {
198 month_max_size = -1;
199 return;
200 }
201 months_width[i] = width;
202 if (width > month_max_size)
203 month_max_size = width;
204 }
205
206 for (i = 0; i < 12; i++)
207 padding_for_month[i] = month_max_size - months_width[i];
208 }
209
210 /*
211 * print name in current style
212 */
213 char *
getname(const char * name)214 getname(const char *name)
215 {
216 if (f_octal || f_octal_escape)
217 return get_octal(name);
218 else if (f_nonprint)
219 return get_printable(name);
220 else
221 return strdup(name);
222 }
223
224 void
printlong(const DISPLAY * dp)225 printlong(const DISPLAY *dp)
226 {
227 struct stat *sp;
228 FTSENT *p;
229 NAMES *np;
230 char buf[20];
231 #ifdef COLORLS
232 int color_printed = 0;
233 #endif
234
235 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
236 (f_longform || f_size)) {
237 xo_emit("{L:total} {:total-blocks/%lu}\n",
238 howmany(dp->btotal, blocksize));
239 }
240
241 xo_open_list("entry");
242 for (p = dp->list; p; p = p->fts_link) {
243 char *name, *type;
244 if (IS_NOPRINT(p))
245 continue;
246 xo_open_instance("entry");
247 sp = p->fts_statp;
248 name = getname(p->fts_name);
249 if (name)
250 xo_emit("{ke:name/%hs}", name);
251 if (f_inode)
252 xo_emit("{t:inode/%*ju} ",
253 dp->s_inode, (uintmax_t)sp->st_ino);
254 if (f_size)
255 xo_emit("{t:blocks/%*jd} ",
256 dp->s_block, howmany(sp->st_blocks, blocksize));
257 strmode(sp->st_mode, buf);
258 aclmode(buf, p);
259 np = p->fts_pointer;
260 xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS);
261 if (f_numericonly) {
262 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*u} {td:user/%-*s}{e:user/%ju} {td:group/%-*s}{e:group/%ju} ",
263 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, sp->st_nlink,
264 dp->s_user, np->user, (uintmax_t)sp->st_uid, dp->s_group, np->group, (uintmax_t)sp->st_gid);
265 } else {
266 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*u} {t:user/%-*s} {t:group/%-*s} ",
267 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, sp->st_nlink,
268 dp->s_user, np->user, dp->s_group, np->group);
269 }
270 if (S_ISBLK(sp->st_mode))
271 asprintf(&type, "block");
272 if (S_ISCHR(sp->st_mode))
273 asprintf(&type, "character");
274 if (S_ISDIR(sp->st_mode))
275 asprintf(&type, "directory");
276 if (S_ISFIFO(sp->st_mode))
277 asprintf(&type, "fifo");
278 if (S_ISLNK(sp->st_mode))
279 asprintf(&type, "symlink");
280 if (S_ISREG(sp->st_mode))
281 asprintf(&type, "regular");
282 if (S_ISSOCK(sp->st_mode))
283 asprintf(&type, "socket");
284 if (S_ISWHT(sp->st_mode))
285 asprintf(&type, "whiteout");
286 xo_emit("{e:type/%s}", type);
287 free(type);
288 if (f_flags)
289 xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags);
290 if (f_label)
291 xo_emit("{t:label/%-*s} ", dp->s_label, np->label);
292 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
293 printdev(dp->s_size, sp->st_rdev);
294 else
295 printsize("size", dp->s_size, sp->st_size);
296 if (f_accesstime)
297 printtime("access-time", sp->st_atime);
298 else if (f_birthtime)
299 printtime("birth-time", sp->st_birthtime);
300 else if (f_statustime)
301 printtime("change-time", sp->st_ctime);
302 else
303 printtime("modify-time", sp->st_mtime);
304 #ifdef COLORLS
305 if (f_color)
306 color_printed = colortype(sp->st_mode);
307 #endif
308
309 if (name) {
310 xo_emit("{dk:name/%hs}", name);
311 free(name);
312 }
313
314 #ifdef COLORLS
315 if (f_color && color_printed)
316 endcolor(0);
317 #endif
318 if (f_type)
319 (void)printtype(sp->st_mode);
320 if (S_ISLNK(sp->st_mode))
321 printlink(p);
322 xo_close_instance("entry");
323 xo_emit("\n");
324 }
325 xo_close_list("entry");
326 }
327
328 void
printstream(const DISPLAY * dp)329 printstream(const DISPLAY *dp)
330 {
331 FTSENT *p;
332 int chcnt;
333
334 xo_open_list("entry");
335 for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
336 if (p->fts_number == NO_PRINT)
337 continue;
338 /* XXX strlen does not take octal escapes into account. */
339 if (strlen(p->fts_name) + chcnt +
340 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
341 xo_emit("\n");
342 chcnt = 0;
343 }
344 xo_open_instance("file");
345 chcnt += printaname(p, dp->s_inode, dp->s_block);
346 xo_close_instance("file");
347 if (p->fts_link) {
348 xo_emit(", ");
349 chcnt += 2;
350 }
351 }
352 xo_close_list("entry");
353 if (chcnt)
354 xo_emit("\n");
355 }
356
357 void
printcol(const DISPLAY * dp)358 printcol(const DISPLAY *dp)
359 {
360 static FTSENT **array;
361 static int lastentries = -1;
362 FTSENT *p;
363 FTSENT **narray;
364 int base;
365 int chcnt;
366 int cnt;
367 int col;
368 int colwidth;
369 int endcol;
370 int num;
371 int numcols;
372 int numrows;
373 int row;
374 int tabwidth;
375
376 if (f_notabs)
377 tabwidth = 1;
378 else
379 tabwidth = 8;
380
381 /*
382 * Have to do random access in the linked list -- build a table
383 * of pointers.
384 */
385 if (dp->entries > lastentries) {
386 if ((narray =
387 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
388 printscol(dp);
389 return;
390 }
391 lastentries = dp->entries;
392 array = narray;
393 }
394 for (p = dp->list, num = 0; p; p = p->fts_link)
395 if (p->fts_number != NO_PRINT)
396 array[num++] = p;
397
398 colwidth = dp->maxlen;
399 if (f_inode)
400 colwidth += dp->s_inode + 1;
401 if (f_size)
402 colwidth += dp->s_block + 1;
403 if (f_type)
404 colwidth += 1;
405
406 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
407 if (termwidth < 2 * colwidth) {
408 printscol(dp);
409 return;
410 }
411 numcols = termwidth / colwidth;
412 numrows = num / numcols;
413 if (num % numcols)
414 ++numrows;
415
416 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
417 (f_longform || f_size)) {
418 xo_emit("{L:total} {:total-blocks/%lu}\n",
419 howmany(dp->btotal, blocksize));
420 }
421
422 xo_open_list("entry");
423 base = 0;
424 for (row = 0; row < numrows; ++row) {
425 endcol = colwidth;
426 if (!f_sortacross)
427 base = row;
428 for (col = 0, chcnt = 0; col < numcols; ++col) {
429 xo_open_instance("entry");
430 chcnt += printaname(array[base], dp->s_inode,
431 dp->s_block);
432 xo_close_instance("entry");
433 if (f_sortacross)
434 base++;
435 else
436 base += numrows;
437 if (base >= num)
438 break;
439 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
440 <= endcol) {
441 if (f_sortacross && col + 1 >= numcols)
442 break;
443 xo_emit(f_notabs ? " " : "\t");
444 chcnt = cnt;
445 }
446 endcol += colwidth;
447 }
448 xo_emit("\n");
449 }
450 xo_close_list("entry");
451 }
452
453 /*
454 * print [inode] [size] name
455 * return # of characters printed, no trailing characters.
456 */
457 static int
printaname(const FTSENT * p,u_long inodefield,u_long sizefield)458 printaname(const FTSENT *p, u_long inodefield, u_long sizefield)
459 {
460 struct stat *sp;
461 int chcnt;
462 #ifdef COLORLS
463 int color_printed = 0;
464 #endif
465
466 sp = p->fts_statp;
467 chcnt = 0;
468 if (f_inode)
469 chcnt += xo_emit("{t:inode/%*ju} ",
470 (int)inodefield, (uintmax_t)sp->st_ino);
471 if (f_size)
472 chcnt += xo_emit("{t:size/%*jd} ",
473 (int)sizefield, howmany(sp->st_blocks, blocksize));
474 #ifdef COLORLS
475 if (f_color)
476 color_printed = colortype(sp->st_mode);
477 #endif
478 chcnt += printname("name", p->fts_name);
479 #ifdef COLORLS
480 if (f_color && color_printed)
481 endcolor(0);
482 #endif
483 if (f_type)
484 chcnt += printtype(sp->st_mode);
485 return (chcnt);
486 }
487
488 /*
489 * Print device special file major and minor numbers.
490 */
491 static void
printdev(size_t width,dev_t dev)492 printdev(size_t width, dev_t dev)
493 {
494 xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev);
495 }
496
497 static size_t
ls_strftime(char * str,size_t len,const char * fmt,const struct tm * tm)498 ls_strftime(char *str, size_t len, const char *fmt, const struct tm *tm)
499 {
500 char *posb, nfmt[BUFSIZ];
501 const char *format = fmt;
502 size_t ret;
503
504 if ((posb = strstr(fmt, "%b")) != NULL) {
505 if (month_max_size == 0) {
506 compute_abbreviated_month_size();
507 }
508 if (month_max_size > 0) {
509 snprintf(nfmt, sizeof(nfmt), "%.*s%s%*s%s",
510 (int)(posb - fmt), fmt,
511 get_abmon(tm->tm_mon),
512 (int)padding_for_month[tm->tm_mon],
513 "",
514 posb + 2);
515 format = nfmt;
516 }
517 }
518 ret = strftime(str, len, format, tm);
519 return (ret);
520 }
521
522 static void
printtime(const char * field,time_t ftime)523 printtime(const char *field, time_t ftime)
524 {
525 char longstring[80];
526 char fmt[BUFSIZ];
527 static time_t now = 0;
528 const char *format;
529 static int d_first = -1;
530
531 if (d_first < 0)
532 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
533 if (now == 0)
534 now = time(NULL);
535
536 #define SIXMONTHS ((365 / 2) * 86400)
537 if (f_timeformat) /* user specified format */
538 format = f_timeformat;
539 else if (f_sectime)
540 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
541 format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
542 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
543 /* mmm dd hh:mm || dd mmm hh:mm */
544 format = d_first ? "%e %b %R" : "%b %e %R";
545 else
546 /* mmm dd yyyy || dd mmm yyyy */
547 format = d_first ? "%e %b %Y" : "%b %e %Y";
548 ls_strftime(longstring, sizeof(longstring), format, localtime(&ftime));
549
550 snprintf(fmt, sizeof(fmt), "{d:%s/%%hs} ", field);
551 xo_attr("value", "%ld", (long) ftime);
552 xo_emit(fmt, longstring);
553 snprintf(fmt, sizeof(fmt), "{en:%s/%%ld}", field);
554 xo_emit(fmt, (long) ftime);
555 }
556
557 static int
printtype(u_int mode)558 printtype(u_int mode)
559 {
560
561 if (f_slash) {
562 if ((mode & S_IFMT) == S_IFDIR) {
563 xo_emit("{D:\\/}{e:type/directory}");
564 return (1);
565 }
566 return (0);
567 }
568
569 switch (mode & S_IFMT) {
570 case S_IFDIR:
571 xo_emit("{D:/\\/}{e:type/directory}");
572 return (1);
573 case S_IFIFO:
574 xo_emit("{D:|}{e:type/fifo}");
575 return (1);
576 case S_IFLNK:
577 xo_emit("{D:@}{e:type/link}");
578 return (1);
579 case S_IFSOCK:
580 xo_emit("{D:=}{e:type/socket}");
581 return (1);
582 case S_IFWHT:
583 xo_emit("{D:%%}{e:type/whiteout}");
584 return (1);
585 default:
586 break;
587 }
588 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
589 xo_emit("{D:*}{e:executable/}");
590 return (1);
591 }
592 return (0);
593 }
594
595 #ifdef COLORLS
596 static int
putch(int c)597 putch(int c)
598 {
599 xo_emit("{D:/%c}", c);
600 return 0;
601 }
602
603 static int
writech(int c)604 writech(int c)
605 {
606 char tmp = (char)c;
607
608 (void)write(STDOUT_FILENO, &tmp, 1);
609 return 0;
610 }
611
612 static void
printcolor(Colors c)613 printcolor(Colors c)
614 {
615 char *ansiseq;
616
617 if (colors[c].bold)
618 tputs(enter_bold, 1, putch);
619
620 if (colors[c].num[0] != -1) {
621 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
622 if (ansiseq)
623 tputs(ansiseq, 1, putch);
624 }
625 if (colors[c].num[1] != -1) {
626 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
627 if (ansiseq)
628 tputs(ansiseq, 1, putch);
629 }
630 }
631
632 static void
endcolor(int sig)633 endcolor(int sig)
634 {
635 tputs(ansi_coloff, 1, sig ? writech : putch);
636 tputs(attrs_off, 1, sig ? writech : putch);
637 }
638
639 static int
colortype(mode_t mode)640 colortype(mode_t mode)
641 {
642 switch (mode & S_IFMT) {
643 case S_IFDIR:
644 if (mode & S_IWOTH)
645 if (mode & S_ISTXT)
646 printcolor(C_WSDIR);
647 else
648 printcolor(C_WDIR);
649 else
650 printcolor(C_DIR);
651 return (1);
652 case S_IFLNK:
653 printcolor(C_LNK);
654 return (1);
655 case S_IFSOCK:
656 printcolor(C_SOCK);
657 return (1);
658 case S_IFIFO:
659 printcolor(C_FIFO);
660 return (1);
661 case S_IFBLK:
662 printcolor(C_BLK);
663 return (1);
664 case S_IFCHR:
665 printcolor(C_CHR);
666 return (1);
667 default:;
668 }
669 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
670 if (mode & S_ISUID)
671 printcolor(C_SUID);
672 else if (mode & S_ISGID)
673 printcolor(C_SGID);
674 else
675 printcolor(C_EXEC);
676 return (1);
677 }
678 return (0);
679 }
680
681 void
parsecolors(const char * cs)682 parsecolors(const char *cs)
683 {
684 int i;
685 int j;
686 size_t len;
687 char c[2];
688 short legacy_warn = 0;
689
690 if (cs == NULL)
691 cs = ""; /* LSCOLORS not set */
692 len = strlen(cs);
693 for (i = 0; i < (int)C_NUMCOLORS; i++) {
694 colors[i].bold = 0;
695
696 if (len <= 2 * (size_t)i) {
697 c[0] = defcolors[2 * i];
698 c[1] = defcolors[2 * i + 1];
699 } else {
700 c[0] = cs[2 * i];
701 c[1] = cs[2 * i + 1];
702 }
703 for (j = 0; j < 2; j++) {
704 /* Legacy colours used 0-7 */
705 if (c[j] >= '0' && c[j] <= '7') {
706 colors[i].num[j] = c[j] - '0';
707 if (!legacy_warn) {
708 xo_warnx("LSCOLORS should use "
709 "characters a-h instead of 0-9 ("
710 "see the manual page)");
711 }
712 legacy_warn = 1;
713 } else if (c[j] >= 'a' && c[j] <= 'h')
714 colors[i].num[j] = c[j] - 'a';
715 else if (c[j] >= 'A' && c[j] <= 'H') {
716 colors[i].num[j] = c[j] - 'A';
717 colors[i].bold = 1;
718 } else if (tolower((unsigned char)c[j]) == 'x')
719 colors[i].num[j] = -1;
720 else {
721 xo_warnx("invalid character '%c' in LSCOLORS"
722 " env var", c[j]);
723 colors[i].num[j] = -1;
724 }
725 }
726 }
727 }
728
729 void
colorquit(int sig)730 colorquit(int sig)
731 {
732 endcolor(sig);
733
734 (void)signal(sig, SIG_DFL);
735 (void)kill(getpid(), sig);
736 }
737
738 #endif /* COLORLS */
739
740 static void
printlink(const FTSENT * p)741 printlink(const FTSENT *p)
742 {
743 int lnklen;
744 char name[MAXPATHLEN + 1];
745 char path[MAXPATHLEN + 1];
746
747 if (p->fts_level == FTS_ROOTLEVEL)
748 (void)snprintf(name, sizeof(name), "%s", p->fts_name);
749 else
750 (void)snprintf(name, sizeof(name),
751 "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
752 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
753 xo_error("\nls: %s: %s\n", name, strerror(errno));
754 return;
755 }
756 path[lnklen] = '\0';
757 xo_emit(" -> ");
758 (void)printname("target", path);
759 }
760
761 static void
printsize(const char * field,size_t width,off_t bytes)762 printsize(const char *field, size_t width, off_t bytes)
763 {
764 char fmt[BUFSIZ];
765
766 if (f_humanval) {
767 /*
768 * Reserve one space before the size and allocate room for
769 * the trailing '\0'.
770 */
771 char buf[HUMANVALSTR_LEN - 1 + 1];
772
773 humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
774 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
775 snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width);
776 xo_attr("value", "%jd", (intmax_t) bytes);
777 xo_emit(fmt, buf);
778 } else { /* with commas */
779 /* This format assignment needed to work round gcc bug. */
780 snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ",
781 field, (int) width, f_thousands ? "'" : "");
782 xo_emit(fmt, (intmax_t) bytes);
783 }
784 }
785
786 /*
787 * Add a + after the standard rwxrwxrwx mode if the file has an
788 * ACL. strmode() reserves space at the end of the string.
789 */
790 static void
aclmode(char * buf,const FTSENT * p)791 aclmode(char *buf, const FTSENT *p)
792 {
793 char name[MAXPATHLEN + 1];
794 int ret, trivial;
795 static dev_t previous_dev = NODEV;
796 static int supports_acls = -1;
797 static int type = ACL_TYPE_ACCESS;
798 acl_t facl;
799
800 /*
801 * XXX: ACLs are not supported on whiteouts and device files
802 * residing on UFS.
803 */
804 if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
805 S_ISWHT(p->fts_statp->st_mode))
806 return;
807
808 if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
809 return;
810
811 if (p->fts_level == FTS_ROOTLEVEL)
812 snprintf(name, sizeof(name), "%s", p->fts_name);
813 else
814 snprintf(name, sizeof(name), "%s/%s",
815 p->fts_parent->fts_accpath, p->fts_name);
816
817 if (previous_dev != p->fts_statp->st_dev) {
818 previous_dev = p->fts_statp->st_dev;
819 supports_acls = 0;
820
821 ret = lpathconf(name, _PC_ACL_NFS4);
822 if (ret > 0) {
823 type = ACL_TYPE_NFS4;
824 supports_acls = 1;
825 } else if (ret < 0 && errno != EINVAL) {
826 xo_warn("%s", name);
827 return;
828 }
829 if (supports_acls == 0) {
830 ret = lpathconf(name, _PC_ACL_EXTENDED);
831 if (ret > 0) {
832 type = ACL_TYPE_ACCESS;
833 supports_acls = 1;
834 } else if (ret < 0 && errno != EINVAL) {
835 xo_warn("%s", name);
836 return;
837 }
838 }
839 }
840 if (supports_acls == 0)
841 return;
842 facl = acl_get_link_np(name, type);
843 if (facl == NULL) {
844 xo_warn("%s", name);
845 return;
846 }
847 if (acl_is_trivial_np(facl, &trivial)) {
848 acl_free(facl);
849 xo_warn("%s", name);
850 return;
851 }
852 if (!trivial)
853 buf[10] = '+';
854 acl_free(facl);
855 }
856