]> git.saurik.com Git - apple/file_cmds.git/blob - ls/print.c
2fdd31c1bb537f97559f4f9f1a88517d04a0a173
[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 <libutil.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <time.h>
70 #include <unistd.h>
71 #ifdef COLORLS
72 #include <ctype.h>
73 #include <termcap.h>
74 #include <signal.h>
75 #endif
76 #include <stdint.h> /* intmax_t */
77 #ifdef __APPLE__
78 #include <get_compat.h>
79 #else
80 #define COMPAT_MODE(a,b) (1)
81 #endif /* __APPLE__ */
82
83 #include "ls.h"
84 #include "extern.h"
85
86 static int printaname(FTSENT *, u_long, u_long);
87 static void printlink(FTSENT *);
88 static void printtime(time_t);
89 static int printtype(u_int);
90 static void printsize(size_t, off_t);
91 #ifdef COLORLS
92 static void endcolor(int);
93 static int colortype(mode_t);
94 #endif
95
96 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
97
98 #ifdef COLORLS
99 /* Most of these are taken from <sys/stat.h> */
100 typedef enum Colors {
101 C_DIR, /* directory */
102 C_LNK, /* symbolic link */
103 C_SOCK, /* socket */
104 C_FIFO, /* pipe */
105 C_EXEC, /* executable */
106 C_BLK, /* block special */
107 C_CHR, /* character special */
108 C_SUID, /* setuid executable */
109 C_SGID, /* setgid executable */
110 C_WSDIR, /* directory writeble to others, with sticky
111 * bit */
112 C_WDIR, /* directory writeble to others, without
113 * sticky bit */
114 C_NUMCOLORS /* just a place-holder */
115 } Colors;
116
117 static const char *defcolors = "exfxcxdxbxegedabagacad";
118
119 /* colors for file types */
120 static struct {
121 int num[2];
122 int bold;
123 } colors[C_NUMCOLORS];
124 #endif
125
126 void
127 printscol(DISPLAY *dp)
128 {
129 FTSENT *p;
130
131 if (COMPAT_MODE("bin/ls", "Unix2003")) {
132 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
133 (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
134 }
135
136 for (p = dp->list; p; p = p->fts_link) {
137 if (IS_NOPRINT(p))
138 continue;
139 (void)printaname(p, dp->s_inode, dp->s_block);
140 (void)putchar('\n');
141 }
142 }
143
144 /*
145 * print name in current style
146 */
147 static int
148 printname(const char *name)
149 {
150 if (f_octal || f_octal_escape)
151 return prn_octal(name);
152 else if (f_nonprint)
153 return prn_printable(name);
154 else
155 return prn_normal(name);
156 }
157
158 /*
159 * print access control list
160 */
161 static struct {
162 acl_perm_t perm;
163 char *name;
164 int flags;
165 #define ACL_PERM_DIR (1<<0)
166 #define ACL_PERM_FILE (1<<1)
167 } acl_perms[] = {
168 {ACL_READ_DATA, "read", ACL_PERM_FILE},
169 {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR},
170 {ACL_WRITE_DATA, "write", ACL_PERM_FILE},
171 {ACL_ADD_FILE, "add_file", ACL_PERM_DIR},
172 {ACL_EXECUTE, "execute", ACL_PERM_FILE},
173 {ACL_SEARCH, "search", ACL_PERM_DIR},
174 {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR},
175 {ACL_APPEND_DATA, "append", ACL_PERM_FILE},
176 {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR},
177 {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR},
178 {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR},
179 {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR},
180 {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
181 {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
182 {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
183 {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
184 {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR},
185 {0, NULL, 0}
186 };
187
188 static struct {
189 acl_flag_t flag;
190 char *name;
191 int flags;
192 } acl_flags[] = {
193 {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR},
194 {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR},
195 {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR},
196 {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR},
197 {0, NULL, 0}
198 };
199
200 static char *
201 uuid_to_name(uuid_t *uu)
202 {
203 #if TARGET_OS_EMBEDDED
204 return strdup("<UNKNOWN>");
205 #else /* !TARGET_OS_EMBEDDED */
206 int is_gid = -1;
207 struct group *tgrp = NULL;
208 struct passwd *tpass = NULL;
209 char *name = NULL;
210 uid_t id;
211
212
213 #define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */
214 name = (char *) malloc(MAXNAMETAG);
215
216 if (NULL == name)
217 err(1, "malloc");
218
219 if (!f_numericonly) {
220 if (0 != mbr_uuid_to_id(*uu, &id, &is_gid))
221 goto errout;
222 }
223
224 switch (is_gid) {
225 case ID_TYPE_UID:
226 tpass = getpwuid(id);
227 if (!tpass) {
228 goto errout;
229 }
230 snprintf(name, MAXNAMETAG, "%s:%s", "user", tpass->pw_name);
231 break;
232 case ID_TYPE_GID:
233 tgrp = getgrgid((gid_t) id);
234 if (!tgrp) {
235 goto errout;
236 }
237 snprintf(name, MAXNAMETAG, "%s:%s", "group", tgrp->gr_name);
238 break;
239 default:
240 goto errout;
241 }
242 return name;
243 errout:
244 if (0 != mbr_uuid_to_string(*uu, name)) {
245 fprintf(stderr, "Unable to translate qualifier on ACL\n");
246 strcpy(name, "<UNKNOWN>");
247 }
248 return name;
249 #endif /* !TARGET_OS_EMBEDDED */
250 }
251
252 static void
253 printxattr(DISPLAY *dp, char *filename, ssize_t xattr)
254 {
255 int flags = XATTR_NOFOLLOW;
256 char *buf = malloc(xattr);
257
258 if (NULL == buf)
259 err(1, "malloc");
260 if (listxattr(filename, buf, xattr, flags) > 0) {
261 char *name;
262 for (name = buf; name < buf+xattr; name += strlen(name) + 1) {
263 ssize_t size = getxattr(filename, name, 0, 0, 0, flags);
264 putchar('\t');
265 printname(name);
266 putchar('\t');
267 printsize(dp->s_size, size);
268 putchar('\n');
269 }
270 }
271 free(buf);
272 }
273
274 static void
275 printacl(acl_t acl, int isdir)
276 {
277 acl_entry_t entry = NULL;
278 int index;
279 uuid_t *applicable;
280 char *name = NULL;
281 acl_tag_t tag;
282 acl_flagset_t flags;
283 acl_permset_t perms;
284 char *type;
285 int i, first;
286
287
288 for (index = 0;
289 acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;
290 index++) {
291 if (acl_get_tag_type(entry, &tag) != 0)
292 continue;
293 if (acl_get_flagset_np(entry, &flags) != 0)
294 continue;
295 if (acl_get_permset(entry, &perms) != 0)
296 continue;
297 if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL)
298 continue;
299 name = uuid_to_name(applicable);
300 acl_free(applicable);
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 if (f_birthtime)
462 printtime(sp->st_birthtime);
463 else
464 printtime(sp->st_mtime);
465 #ifdef COLORLS
466 if (f_color)
467 color_printed = colortype(sp->st_mode);
468 #endif
469 (void)printname(p->fts_name);
470 #ifdef COLORLS
471 if (f_color && color_printed)
472 endcolor(0);
473 #endif
474 if (f_type)
475 (void)printtype(sp->st_mode);
476 if (S_ISLNK(sp->st_mode))
477 printlink(p);
478 (void)putchar('\n');
479 #ifdef __APPLE__
480 if (f_xattr && xattr) {
481 printxattr(dp, filename, xattr);
482 }
483 if (acl != NULL) {
484 if (f_acl)
485 printacl(acl, S_ISDIR(sp->st_mode));
486 acl_free(acl);
487 acl = NULL;
488 }
489 #endif /* __APPLE__ */
490 }
491 }
492
493 void
494 printstream(DISPLAY *dp)
495 {
496 FTSENT *p;
497 extern int termwidth;
498 int chcnt;
499
500 for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
501 if (p->fts_number == NO_PRINT)
502 continue;
503 if (strlen(p->fts_name) + chcnt +
504 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
505 putchar('\n');
506 chcnt = 0;
507 }
508 chcnt += printaname(p, dp->s_inode, dp->s_block);
509 if (p->fts_link) {
510 printf(", ");
511 chcnt += 2;
512 }
513 }
514 if (chcnt)
515 putchar('\n');
516 }
517
518 void
519 printcol(DISPLAY *dp)
520 {
521 extern int termwidth;
522 static FTSENT **array;
523 static int lastentries = -1;
524 FTSENT *p;
525 int base;
526 int chcnt;
527 int cnt;
528 int col;
529 int colwidth;
530 int endcol;
531 int num;
532 int numcols;
533 int numrows;
534 int row;
535 int tabwidth;
536
537 if (f_notabs)
538 tabwidth = 1;
539 else
540 tabwidth = 8;
541
542 /*
543 * Have to do random access in the linked list -- build a table
544 * of pointers.
545 */
546 if (dp->entries > lastentries) {
547 lastentries = dp->entries;
548 if ((array =
549 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
550 warn(NULL);
551 printscol(dp);
552 }
553 }
554 for (p = dp->list, num = 0; p; p = p->fts_link)
555 if (p->fts_number != NO_PRINT)
556 array[num++] = p;
557
558 colwidth = dp->maxlen;
559 if (f_inode)
560 colwidth += dp->s_inode + 1;
561 if (f_size)
562 colwidth += dp->s_block + 1;
563 if (f_type)
564 colwidth += 1;
565
566 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
567 if (termwidth < 2 * colwidth) {
568 printscol(dp);
569 return;
570 }
571 numcols = termwidth / colwidth;
572 numrows = num / numcols;
573 if (num % numcols)
574 ++numrows;
575
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 }