file_cmds-184.tar.gz
[apple/file_cmds.git] / 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 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #if 0
38 #ifndef lint
39 static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94";
40 #endif /* not lint */
41 #endif
42 #include <sys/cdefs.h>
43 __RCSID("$FreeBSD: src/bin/ls/print.c,v 1.57 2002/08/29 14:29:09 keramida Exp $");
44
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #ifdef __APPLE__
48 #include <sys/acl.h>
49 #include <sys/xattr.h>
50 #include <sys/types.h>
51 #include <grp.h>
52 #include <pwd.h>
53 #include <membership.h>
54 #include <membershipPriv.h>
55 #include <uuid/uuid.h>
56 #endif
57
58 #include <err.h>
59 #include <errno.h>
60 #include <fts.h>
61 #include <math.h>
62 #include <langinfo.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <time.h>
67 #include <unistd.h>
68 #ifdef COLORLS
69 #include <ctype.h>
70 #include <termcap.h>
71 #include <signal.h>
72 #endif
73
74 #ifdef __APPLE__
75 #include <get_compat.h>
76 #else
77 #define COMPAT_MODE(a,b) (1)
78 #endif /* __APPLE__ */
79
80 #include "ls.h"
81 #include "extern.h"
82
83 static int printaname(FTSENT *, u_long, u_long);
84 static void printlink(FTSENT *);
85 static void printtime(time_t);
86 static int printtype(u_int);
87 static void printsize(size_t, off_t);
88 #ifdef COLORLS
89 static void endcolor(int);
90 static int colortype(mode_t);
91 #endif
92
93 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
94
95 #ifdef COLORLS
96 /* Most of these are taken from <sys/stat.h> */
97 typedef enum Colors {
98 C_DIR, /* directory */
99 C_LNK, /* symbolic link */
100 C_SOCK, /* socket */
101 C_FIFO, /* pipe */
102 C_EXEC, /* executable */
103 C_BLK, /* block special */
104 C_CHR, /* character special */
105 C_SUID, /* setuid executable */
106 C_SGID, /* setgid executable */
107 C_WSDIR, /* directory writeble to others, with sticky
108 * bit */
109 C_WDIR, /* directory writeble to others, without
110 * sticky bit */
111 C_NUMCOLORS /* just a place-holder */
112 } Colors;
113
114 static const char *defcolors = "exfxcxdxbxegedabagacad";
115
116 /* colors for file types */
117 static struct {
118 int num[2];
119 int bold;
120 } colors[C_NUMCOLORS];
121 #endif
122
123 void
124 printscol(DISPLAY *dp)
125 {
126 FTSENT *p;
127
128 if (COMPAT_MODE("bin/ls", "Unix2003")) {
129 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
130 (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
131 }
132
133 for (p = dp->list; p; p = p->fts_link) {
134 if (IS_NOPRINT(p))
135 continue;
136 (void)printaname(p, dp->s_inode, dp->s_block);
137 (void)putchar('\n');
138 }
139 }
140
141 /*
142 * print name in current style
143 */
144 static int
145 printname(const char *name)
146 {
147 if (f_octal || f_octal_escape)
148 return prn_octal(name);
149 else if (f_nonprint)
150 return prn_printable(name);
151 else
152 return prn_normal(name);
153 }
154
155 /*
156 * print access control list
157 */
158 static struct {
159 acl_perm_t perm;
160 char *name;
161 int flags;
162 #define ACL_PERM_DIR (1<<0)
163 #define ACL_PERM_FILE (1<<1)
164 } acl_perms[] = {
165 {ACL_READ_DATA, "read", ACL_PERM_FILE},
166 {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR},
167 {ACL_WRITE_DATA, "write", ACL_PERM_FILE},
168 {ACL_ADD_FILE, "add_file", ACL_PERM_DIR},
169 {ACL_EXECUTE, "execute", ACL_PERM_FILE},
170 {ACL_SEARCH, "search", ACL_PERM_DIR},
171 {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR},
172 {ACL_APPEND_DATA, "append", ACL_PERM_FILE},
173 {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR},
174 {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR},
175 {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR},
176 {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR},
177 {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
178 {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
179 {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
180 {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
181 {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR},
182 {0, NULL, 0}
183 };
184
185 static struct {
186 acl_flag_t flag;
187 char *name;
188 int flags;
189 } acl_flags[] = {
190 {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR},
191 {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR},
192 {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR},
193 {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR},
194 {0, NULL, 0}
195 };
196
197 static char *
198 uuid_to_name(uuid_t *uu)
199 {
200 int is_gid = -1;
201 struct group *tgrp = NULL;
202 struct passwd *tpass = NULL;
203 char *name = NULL;
204 uid_t id;
205
206
207 #define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */
208 name = (char *) malloc(MAXNAMETAG);
209
210 if (NULL == name)
211 err(1, "malloc");
212
213 if (!f_numericonly) {
214 if (0 != mbr_uuid_to_id(*uu, &id, &is_gid))
215 goto errout;
216 }
217
218 switch (is_gid) {
219 case ID_TYPE_UID:
220 tpass = getpwuid(id);
221 if (!tpass) {
222 goto errout;
223 }
224 snprintf(name, MAXNAMETAG, "%s:%s", "user", tpass->pw_name);
225 break;
226 case ID_TYPE_GID:
227 tgrp = getgrgid((gid_t) id);
228 if (!tgrp) {
229 goto errout;
230 }
231 snprintf(name, MAXNAMETAG, "%s:%s", "group", tgrp->gr_name);
232 break;
233 default:
234 goto errout;
235 }
236 return name;
237 errout:
238 if (0 != mbr_uuid_to_string(*uu, name)) {
239 fprintf(stderr, "Unable to translate qualifier on ACL\n");
240 strcpy(name, "<UNKNOWN>");
241 }
242 return name;
243 }
244
245 static void
246 printxattr(DISPLAY *dp, char *filename, ssize_t xattr)
247 {
248 int flags = XATTR_NOFOLLOW;
249 char *buf = malloc(xattr);
250
251 if (NULL == buf)
252 err(1, "malloc");
253 if (listxattr(filename, buf, xattr, flags) > 0) {
254 char *name;
255 for (name = buf; name < buf+xattr; name += strlen(name) + 1) {
256 ssize_t size = getxattr(filename, name, 0, 0, 0, flags);
257 putchar('\t');
258 printname(name);
259 putchar('\t');
260 printsize(dp->s_size, size);
261 putchar('\n');
262 }
263 }
264 free(buf);
265 }
266
267 static void
268 printacl(acl_t acl, int isdir)
269 {
270 acl_entry_t entry = NULL;
271 int index;
272 uuid_t *applicable;
273 char *name = NULL;
274 acl_tag_t tag;
275 acl_flagset_t flags;
276 acl_permset_t perms;
277 char *type;
278 int i, first;
279
280
281 for (index = 0;
282 acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;
283 index++) {
284 if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL)
285 continue;
286 if (acl_get_tag_type(entry, &tag) != 0)
287 continue;
288 if (acl_get_flagset_np(entry, &flags) != 0)
289 continue;
290 if (acl_get_permset(entry, &perms) != 0)
291 continue;
292
293 name = uuid_to_name(applicable);
294 acl_free(applicable);
295
296 switch(tag) {
297 case ACL_EXTENDED_ALLOW:
298 type = "allow";
299 break;
300 case ACL_EXTENDED_DENY:
301 type = "deny";
302 break;
303 default:
304 type = "unknown";
305 }
306
307 (void)printf(" %d: %s%s %s ",
308 index,
309 name,
310 acl_get_flag_np(flags, ACL_ENTRY_INHERITED) ? " inherited" : "",
311 type);
312
313 if (name)
314 free(name);
315
316 for (i = 0, first = 0; acl_perms[i].name != NULL; i++) {
317 if (acl_get_perm_np(perms, acl_perms[i].perm) == 0)
318 continue;
319 if (!(acl_perms[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
320 continue;
321 (void)printf("%s%s", first++ ? "," : "", acl_perms[i].name);
322 }
323 for (i = 0; acl_flags[i].name != NULL; i++) {
324 if (acl_get_flag_np(flags, acl_flags[i].flag) == 0)
325 continue;
326 if (!(acl_flags[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
327 continue;
328 (void)printf("%s%s", first++ ? "," : "", acl_flags[i].name);
329 }
330
331 (void)putchar('\n');
332 }
333
334 }
335
336 void
337 printlong(DISPLAY *dp)
338 {
339 struct stat *sp;
340 FTSENT *p;
341 NAMES *np;
342 char buf[20];
343 #ifdef __APPLE__
344 acl_t acl = NULL;
345 acl_entry_t dummy;
346 char full_path[MAXPATHLEN];
347 char *filename;
348 ssize_t xattr = 0;
349 char str[2];
350 #endif
351 #ifdef COLORLS
352 int color_printed = 0;
353 #endif
354
355 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
356 (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
357
358 for (p = dp->list; p; p = p->fts_link) {
359 if (IS_NOPRINT(p))
360 continue;
361 sp = p->fts_statp;
362 if (f_inode)
363 #if _DARWIN_FEATURE_64_BIT_INODE
364 (void)printf("%*llu ", dp->s_inode, (u_quad_t)sp->st_ino);
365 #else
366 (void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
367 #endif
368 if (f_size)
369 (void)printf("%*qu ",
370 dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize));
371 strmode(sp->st_mode, buf);
372 np = p->fts_pointer;
373 #ifdef __APPLE__
374 buf[10] = '\0'; /* make +/@ abut the mode */
375 filename = p->fts_name;
376 if (p->fts_level != FTS_ROOTLEVEL)
377 {
378 snprintf(full_path, sizeof full_path, "%s/%s",
379 p->fts_parent->fts_accpath, p->fts_name);
380 filename = full_path;
381 }
382 /* symlinks can not have ACLs */
383 acl = acl_get_link_np(filename, ACL_TYPE_EXTENDED);
384 if (acl && acl_get_entry(acl, ACL_FIRST_ENTRY, &dummy) == -1) {
385 acl_free(acl);
386 acl = NULL;
387 }
388 xattr = listxattr(filename, NULL, 0, XATTR_NOFOLLOW);
389 if (xattr < 0)
390 xattr = 0;
391 str[1] = '\0';
392 if (xattr > 0)
393 str[0] = '@';
394 else if (acl != NULL)
395 str[0] = '+';
396 else
397 str[0] = ' ';
398 #endif /* __APPLE__ */
399 if (f_group && f_owner) { /* means print neither */
400 #ifdef __APPLE__
401 (void)printf("%s%s %*u ", buf, str, dp->s_nlink,
402 sp->st_nlink);
403 #else /* ! __APPLE__ */
404 (void)printf("%s %*u ", buf, dp->s_nlink,
405 sp->st_nlink);
406 #endif /* __APPLE__ */
407 }
408 else if (f_group) {
409 #ifdef __APPLE__
410 (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink,
411 sp->st_nlink, dp->s_group, np->group);
412 #else /* ! __APPLE__ */
413 (void)printf("%s %*u %-*s ", buf, dp->s_nlink,
414 sp->st_nlink, dp->s_group, np->group);
415 #endif /* __APPLE__ */
416 }
417 else if (f_owner) {
418 #ifdef __APPLE__
419 (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink,
420 sp->st_nlink, dp->s_user, np->user);
421 #else /* ! __APPLE__ */
422 (void)printf("%s %*u %-*s ", buf, dp->s_nlink,
423 sp->st_nlink, dp->s_user, np->user);
424 #endif /* __APPLE__ */
425 }
426 else {
427 #ifdef __APPLE__
428 (void)printf("%s%s %*u %-*s %-*s ", buf, str, dp->s_nlink,
429 sp->st_nlink, dp->s_user, np->user, dp->s_group,
430 np->group);
431 #else /* ! __APPLE__ */
432 (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink,
433 sp->st_nlink, dp->s_user, np->user, dp->s_group,
434 np->group);
435 #endif /* ! __APPLE__ */
436 }
437 if (f_flags)
438 (void)printf("%-*s ", dp->s_flags, np->flags);
439 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
440 if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
441 (void)printf("%3d, 0x%08x ",
442 major(sp->st_rdev),
443 (u_int)minor(sp->st_rdev));
444 else
445 (void)printf("%3d, %3d ",
446 major(sp->st_rdev), minor(sp->st_rdev));
447 else if (dp->bcfile)
448 (void)printf("%*s%*qu ",
449 8 - dp->s_size, "", dp->s_size, (u_int64_t)sp->st_size);
450 else
451 printsize(dp->s_size, sp->st_size);
452 if (f_accesstime)
453 printtime(sp->st_atime);
454 else if (f_statustime)
455 printtime(sp->st_ctime);
456 else
457 printtime(sp->st_mtime);
458 #ifdef COLORLS
459 if (f_color)
460 color_printed = colortype(sp->st_mode);
461 #endif
462 (void)printname(p->fts_name);
463 #ifdef COLORLS
464 if (f_color && color_printed)
465 endcolor(0);
466 #endif
467 if (f_type)
468 (void)printtype(sp->st_mode);
469 if (S_ISLNK(sp->st_mode))
470 printlink(p);
471 (void)putchar('\n');
472 #ifdef __APPLE__
473 if (f_xattr && xattr) {
474 printxattr(dp, filename, xattr);
475 }
476 if (acl != NULL) {
477 if (f_acl)
478 printacl(acl, S_ISDIR(sp->st_mode));
479 acl_free(acl);
480 acl = NULL;
481 }
482 #endif /* __APPLE__ */
483 }
484 }
485
486 void
487 printstream(DISPLAY *dp)
488 {
489 FTSENT *p;
490 extern int termwidth;
491 int chcnt;
492
493 for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
494 if (p->fts_number == NO_PRINT)
495 continue;
496 if (strlen(p->fts_name) + chcnt +
497 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
498 putchar('\n');
499 chcnt = 0;
500 }
501 chcnt += printaname(p, dp->s_inode, dp->s_block);
502 if (p->fts_link) {
503 printf(", ");
504 chcnt += 2;
505 }
506 }
507 if (chcnt)
508 putchar('\n');
509 }
510
511 void
512 printcol(DISPLAY *dp)
513 {
514 extern int termwidth;
515 static FTSENT **array;
516 static int lastentries = -1;
517 FTSENT *p;
518 int base;
519 int chcnt;
520 int cnt;
521 int col;
522 int colwidth;
523 int endcol;
524 int num;
525 int numcols;
526 int numrows;
527 int row;
528 int tabwidth;
529
530 if (f_notabs)
531 tabwidth = 1;
532 else
533 tabwidth = 8;
534
535 /*
536 * Have to do random access in the linked list -- build a table
537 * of pointers.
538 */
539 if (dp->entries > lastentries) {
540 lastentries = dp->entries;
541 if ((array =
542 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
543 warn(NULL);
544 printscol(dp);
545 }
546 }
547 for (p = dp->list, num = 0; p; p = p->fts_link)
548 if (p->fts_number != NO_PRINT)
549 array[num++] = p;
550
551 colwidth = dp->maxlen;
552 if (f_inode)
553 colwidth += dp->s_inode + 1;
554 if (f_size)
555 colwidth += dp->s_block + 1;
556 if (f_type)
557 colwidth += 1;
558
559 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
560 if (termwidth < 2 * colwidth) {
561 printscol(dp);
562 return;
563 }
564 numcols = termwidth / colwidth;
565 numrows = num / numcols;
566 if (num % numcols)
567 ++numrows;
568
569 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
570 (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
571
572 base = 0;
573 for (row = 0; row < numrows; ++row) {
574 endcol = colwidth;
575 if (!f_sortacross)
576 base = row;
577 for (col = 0, chcnt = 0; col < numcols; ++col) {
578 chcnt += printaname(array[base], dp->s_inode,
579 dp->s_block);
580 if (f_sortacross)
581 base++;
582 else
583 base += numrows;
584 if (base >= num)
585 break;
586 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
587 <= endcol) {
588 if (f_sortacross && col + 1 >= numcols)
589 break;
590 (void)putchar(f_notabs ? ' ' : '\t');
591 chcnt = cnt;
592 }
593 endcol += colwidth;
594 }
595 (void)putchar('\n');
596 }
597 }
598
599 /*
600 * print [inode] [size] name
601 * return # of characters printed, no trailing characters.
602 */
603 static int
604 printaname(FTSENT *p, u_long inodefield, u_long sizefield)
605 {
606 struct stat *sp;
607 int chcnt;
608 #ifdef COLORLS
609 int color_printed = 0;
610 #endif
611
612 sp = p->fts_statp;
613 chcnt = 0;
614 if (f_inode)
615 chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
616 if (f_size)
617 chcnt += printf("%*qu ",
618 (int)sizefield, (u_int64_t)howmany(sp->st_blocks, blocksize));
619 #ifdef COLORLS
620 if (f_color)
621 color_printed = colortype(sp->st_mode);
622 #endif
623 chcnt += printname(p->fts_name);
624 #ifdef COLORLS
625 if (f_color && color_printed)
626 endcolor(0);
627 #endif
628 if (f_type)
629 chcnt += printtype(sp->st_mode);
630 return (chcnt);
631 }
632
633 static void
634 printtime(time_t ftime)
635 {
636 char longstring[80];
637 static time_t now;
638 const char *format;
639 static int d_first = -1;
640
641 if (d_first < 0)
642 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
643 if (now == 0)
644 now = time(NULL);
645
646 #define SIXMONTHS ((365 / 2) * 86400)
647 if (f_sectime)
648 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
649 format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
650 else if (COMPAT_MODE("bin/ls", "Unix2003")) {
651 if (ftime + SIXMONTHS > now && ftime <= now)
652 /* mmm dd hh:mm || dd mmm hh:mm */
653 format = d_first ? "%e %b %R " : "%b %e %R ";
654 else
655 /* mmm dd yyyy || dd mmm yyyy */
656 format = d_first ? "%e %b %Y " : "%b %e %Y ";
657 }
658 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
659 /* mmm dd hh:mm || dd mmm hh:mm */
660 format = d_first ? "%e %b %R " : "%b %e %R ";
661 else
662 /* mmm dd yyyy || dd mmm yyyy */
663 format = d_first ? "%e %b %Y " : "%b %e %Y ";
664 strftime(longstring, sizeof(longstring), format, localtime(&ftime));
665 fputs(longstring, stdout);
666 }
667
668 static int
669 printtype(u_int mode)
670 {
671
672 if (f_slash) {
673 if ((mode & S_IFMT) == S_IFDIR) {
674 (void)putchar('/');
675 return (1);
676 }
677 return (0);
678 }
679
680 switch (mode & S_IFMT) {
681 case S_IFDIR:
682 (void)putchar('/');
683 return (1);
684 case S_IFIFO:
685 (void)putchar('|');
686 return (1);
687 case S_IFLNK:
688 (void)putchar('@');
689 return (1);
690 case S_IFSOCK:
691 (void)putchar('=');
692 return (1);
693 case S_IFWHT:
694 (void)putchar('%');
695 return (1);
696 default:
697 break;
698 }
699 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
700 (void)putchar('*');
701 return (1);
702 }
703 return (0);
704 }
705
706 #ifdef COLORLS
707 static int
708 putch(int c)
709 {
710 (void)putchar(c);
711 return 0;
712 }
713
714 static int
715 writech(int c)
716 {
717 char tmp = c;
718
719 (void)write(STDOUT_FILENO, &tmp, 1);
720 return 0;
721 }
722
723 static void
724 printcolor(Colors c)
725 {
726 char *ansiseq;
727
728 if (colors[c].bold)
729 tputs(enter_bold, 1, putch);
730
731 if (colors[c].num[0] != -1) {
732 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
733 if (ansiseq)
734 tputs(ansiseq, 1, putch);
735 }
736 if (colors[c].num[1] != -1) {
737 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
738 if (ansiseq)
739 tputs(ansiseq, 1, putch);
740 }
741 }
742
743 static void
744 endcolor(int sig)
745 {
746 tputs(ansi_coloff, 1, sig ? writech : putch);
747 tputs(attrs_off, 1, sig ? writech : putch);
748 }
749
750 static int
751 colortype(mode_t mode)
752 {
753 switch (mode & S_IFMT) {
754 case S_IFDIR:
755 if (mode & S_IWOTH)
756 if (mode & S_ISTXT)
757 printcolor(C_WSDIR);
758 else
759 printcolor(C_WDIR);
760 else
761 printcolor(C_DIR);
762 return (1);
763 case S_IFLNK:
764 printcolor(C_LNK);
765 return (1);
766 case S_IFSOCK:
767 printcolor(C_SOCK);
768 return (1);
769 case S_IFIFO:
770 printcolor(C_FIFO);
771 return (1);
772 case S_IFBLK:
773 printcolor(C_BLK);
774 return (1);
775 case S_IFCHR:
776 printcolor(C_CHR);
777 return (1);
778 }
779 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
780 if (mode & S_ISUID)
781 printcolor(C_SUID);
782 else if (mode & S_ISGID)
783 printcolor(C_SGID);
784 else
785 printcolor(C_EXEC);
786 return (1);
787 }
788 return (0);
789 }
790
791 void
792 parsecolors(const char *cs)
793 {
794 int i;
795 int j;
796 int len;
797 char c[2];
798 short legacy_warn = 0;
799
800 if (cs == NULL)
801 cs = ""; /* LSCOLORS not set */
802 len = strlen(cs);
803 for (i = 0; i < C_NUMCOLORS; i++) {
804 colors[i].bold = 0;
805
806 if (len <= 2 * i) {
807 c[0] = defcolors[2 * i];
808 c[1] = defcolors[2 * i + 1];
809 } else {
810 c[0] = cs[2 * i];
811 c[1] = cs[2 * i + 1];
812 }
813 for (j = 0; j < 2; j++) {
814 /* Legacy colours used 0-7 */
815 if (c[j] >= '0' && c[j] <= '7') {
816 colors[i].num[j] = c[j] - '0';
817 if (!legacy_warn) {
818 fprintf(stderr,
819 "warn: LSCOLORS should use "
820 "characters a-h instead of 0-9 ("
821 "see the manual page)\n");
822 }
823 legacy_warn = 1;
824 } else if (c[j] >= 'a' && c[j] <= 'h')
825 colors[i].num[j] = c[j] - 'a';
826 else if (c[j] >= 'A' && c[j] <= 'H') {
827 colors[i].num[j] = c[j] - 'A';
828 colors[i].bold = 1;
829 } else if (tolower((unsigned char)c[j] == 'x'))
830 colors[i].num[j] = -1;
831 else {
832 fprintf(stderr,
833 "error: invalid character '%c' in LSCOLORS"
834 " env var\n", c[j]);
835 colors[i].num[j] = -1;
836 }
837 }
838 }
839 }
840
841 void
842 colorquit(int sig)
843 {
844 endcolor(sig);
845
846 (void)signal(sig, SIG_DFL);
847 (void)kill(getpid(), sig);
848 }
849
850 #endif /* COLORLS */
851
852 static void
853 printlink(FTSENT *p)
854 {
855 int lnklen;
856 char name[MAXPATHLEN + 1];
857 char path[MAXPATHLEN + 1];
858
859 if (p->fts_level == FTS_ROOTLEVEL)
860 (void)snprintf(name, sizeof(name), "%s", p->fts_name);
861 else
862 (void)snprintf(name, sizeof(name),
863 "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
864 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
865 (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
866 return;
867 }
868 path[lnklen] = '\0';
869 (void)printf(" -> ");
870 (void)printname(path);
871 }
872
873 static void
874 printsize(size_t width, off_t bytes)
875 {
876
877 if (f_humanval) {
878 char buf[5];
879
880 humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
881 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
882 (void)printf("%5s ", buf);
883 } else
884 (void)printf("%*jd ", (u_int)width, bytes);
885 }