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