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