]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/ls/print.c
Merge branch 'releng/11.3' into releng-CDN/11.3
[FreeBSD/FreeBSD.git] / bin / ls / print.c
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
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
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 *
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
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
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 *
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
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/%*ju} {td:user/%-*s}{e:user/%ju}  {td:group/%-*s}{e:group/%ju}  ",
263                                 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)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/%*ju} {t:user/%-*s}  {t:group/%-*s}  ",
267                                 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)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
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
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
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
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
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
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
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
597 putch(int c)
598 {
599         xo_emit("{D:/%c}", c);
600         return 0;
601 }
602
603 static int
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
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
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
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
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
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
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
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
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 }