]>
Commit | Line | Data |
---|---|---|
44a7a5ab A |
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 | ||
44a7a5ab | 37 | #if 0 |
6c780a1f A |
38 | #ifndef lint |
39 | static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; | |
44a7a5ab | 40 | #endif /* not lint */ |
6c780a1f A |
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 $"); | |
44a7a5ab A |
44 | |
45 | #include <sys/param.h> | |
46 | #include <sys/stat.h> | |
c59d3020 A |
47 | #ifdef __APPLE__ |
48 | #include <sys/acl.h> | |
864a4b6e | 49 | #include <sys/xattr.h> |
c59d3020 A |
50 | #include <sys/types.h> |
51 | #include <grp.h> | |
52 | #include <pwd.h> | |
4d0bb651 | 53 | #include <TargetConditionals.h> |
b1857959 | 54 | #if !TARGET_OS_EMBEDDED |
c59d3020 A |
55 | #include <membership.h> |
56 | #include <membershipPriv.h> | |
b1857959 | 57 | #endif |
c59d3020 A |
58 | #include <uuid/uuid.h> |
59 | #endif | |
44a7a5ab A |
60 | |
61 | #include <err.h> | |
62 | #include <errno.h> | |
63 | #include <fts.h> | |
6c780a1f | 64 | #include <math.h> |
6c780a1f | 65 | #include <langinfo.h> |
686e1a44 | 66 | #include <libutil.h> |
44a7a5ab A |
67 | #include <stdio.h> |
68 | #include <stdlib.h> | |
69 | #include <string.h> | |
70 | #include <time.h> | |
44a7a5ab | 71 | #include <unistd.h> |
6c780a1f A |
72 | #ifdef COLORLS |
73 | #include <ctype.h> | |
74 | #include <termcap.h> | |
75 | #include <signal.h> | |
76 | #endif | |
40bf83fe | 77 | #include <stdint.h> /* intmax_t */ |
4d0bb651 | 78 | #include <assert.h> |
864a4b6e A |
79 | #ifdef __APPLE__ |
80 | #include <get_compat.h> | |
81 | #else | |
82 | #define COMPAT_MODE(a,b) (1) | |
83 | #endif /* __APPLE__ */ | |
84 | ||
44a7a5ab A |
85 | #include "ls.h" |
86 | #include "extern.h" | |
87 | ||
6c780a1f A |
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 | |
44a7a5ab A |
97 | |
98 | #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) | |
99 | ||
6c780a1f A |
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 | ||
44a7a5ab | 128 | void |
6c780a1f | 129 | printscol(DISPLAY *dp) |
44a7a5ab A |
130 | { |
131 | FTSENT *p; | |
132 | ||
864a4b6e A |
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 | ||
44a7a5ab A |
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 | ||
6c780a1f A |
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 | |
864a4b6e | 157 | return prn_normal(name); |
6c780a1f A |
158 | } |
159 | ||
c59d3020 A |
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 | { | |
b1857959 A |
205 | #if TARGET_OS_EMBEDDED |
206 | return strdup("<UNKNOWN>"); | |
207 | #else /* !TARGET_OS_EMBEDDED */ | |
c59d3020 A |
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 | ||
864a4b6e A |
221 | if (!f_numericonly) { |
222 | if (0 != mbr_uuid_to_id(*uu, &id, &is_gid)) | |
c59d3020 | 223 | goto errout; |
864a4b6e | 224 | } |
c59d3020 A |
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: | |
864a4b6e | 242 | goto errout; |
c59d3020 A |
243 | } |
244 | return name; | |
245 | errout: | |
4d0bb651 | 246 | uuid_unparse_upper(*uu, name); |
c59d3020 | 247 | return name; |
b1857959 | 248 | #endif /* !TARGET_OS_EMBEDDED */ |
c59d3020 A |
249 | } |
250 | ||
864a4b6e A |
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 | ||
c59d3020 A |
273 | static void |
274 | printacl(acl_t acl, int isdir) | |
275 | { | |
864a4b6e | 276 | acl_entry_t entry = NULL; |
c59d3020 A |
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++) { | |
c59d3020 A |
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; | |
686e1a44 A |
296 | if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL) |
297 | continue; | |
c59d3020 A |
298 | name = uuid_to_name(applicable); |
299 | acl_free(applicable); | |
c59d3020 A |
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 | ||
44a7a5ab | 340 | void |
6c780a1f | 341 | printlong(DISPLAY *dp) |
44a7a5ab A |
342 | { |
343 | struct stat *sp; | |
344 | FTSENT *p; | |
345 | NAMES *np; | |
346 | char buf[20]; | |
c59d3020 A |
347 | #ifdef __APPLE__ |
348 | acl_t acl = NULL; | |
864a4b6e | 349 | acl_entry_t dummy; |
c59d3020 | 350 | char full_path[MAXPATHLEN]; |
864a4b6e A |
351 | char *filename; |
352 | ssize_t xattr = 0; | |
353 | char str[2]; | |
c59d3020 | 354 | #endif |
6c780a1f A |
355 | #ifdef COLORLS |
356 | int color_printed = 0; | |
357 | #endif | |
44a7a5ab A |
358 | |
359 | if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) | |
6c780a1f | 360 | (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize)); |
44a7a5ab A |
361 | |
362 | for (p = dp->list; p; p = p->fts_link) { | |
363 | if (IS_NOPRINT(p)) | |
364 | continue; | |
365 | sp = p->fts_statp; | |
864a4b6e A |
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 | |
6c780a1f | 370 | (void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino); |
864a4b6e | 371 | #endif |
44a7a5ab | 372 | if (f_size) |
6c780a1f A |
373 | (void)printf("%*qu ", |
374 | dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize)); | |
375 | strmode(sp->st_mode, buf); | |
44a7a5ab | 376 | np = p->fts_pointer; |
c59d3020 | 377 | #ifdef __APPLE__ |
864a4b6e A |
378 | buf[10] = '\0'; /* make +/@ abut the mode */ |
379 | filename = p->fts_name; | |
380 | if (p->fts_level != FTS_ROOTLEVEL) | |
c59d3020 A |
381 | { |
382 | snprintf(full_path, sizeof full_path, "%s/%s", | |
864a4b6e A |
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); | |
c59d3020 | 410 | #endif /* __APPLE__ */ |
864a4b6e A |
411 | } |
412 | else if (f_group) { | |
c59d3020 | 413 | #ifdef __APPLE__ |
864a4b6e | 414 | (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink, |
c59d3020 A |
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); | |
864a4b6e A |
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); | |
c59d3020 A |
428 | #endif /* __APPLE__ */ |
429 | } | |
430 | else { | |
431 | #ifdef __APPLE__ | |
864a4b6e | 432 | (void)printf("%s%s %*u %-*s %-*s ", buf, str, dp->s_nlink, |
c59d3020 A |
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 | } | |
44a7a5ab A |
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)) | |
6c780a1f A |
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); | |
44a7a5ab | 454 | else |
6c780a1f | 455 | printsize(dp->s_size, sp->st_size); |
44a7a5ab A |
456 | if (f_accesstime) |
457 | printtime(sp->st_atime); | |
458 | else if (f_statustime) | |
459 | printtime(sp->st_ctime); | |
40bf83fe A |
460 | else if (f_birthtime) |
461 | printtime(sp->st_birthtime); | |
44a7a5ab A |
462 | else |
463 | printtime(sp->st_mtime); | |
6c780a1f A |
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 | |
44a7a5ab A |
473 | if (f_type) |
474 | (void)printtype(sp->st_mode); | |
475 | if (S_ISLNK(sp->st_mode)) | |
476 | printlink(p); | |
477 | (void)putchar('\n'); | |
c59d3020 | 478 | #ifdef __APPLE__ |
864a4b6e A |
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 | } | |
c59d3020 | 488 | #endif /* __APPLE__ */ |
44a7a5ab A |
489 | } |
490 | } | |
491 | ||
492 | void | |
6c780a1f A |
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) | |
44a7a5ab A |
519 | { |
520 | extern int termwidth; | |
521 | static FTSENT **array; | |
522 | static int lastentries = -1; | |
523 | FTSENT *p; | |
6c780a1f A |
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; | |
44a7a5ab | 535 | |
6c780a1f A |
536 | if (f_notabs) |
537 | tabwidth = 1; | |
538 | else | |
539 | tabwidth = 8; | |
44a7a5ab A |
540 | |
541 | /* | |
542 | * Have to do random access in the linked list -- build a table | |
543 | * of pointers. | |
544 | */ | |
4d0bb651 | 545 | if ((lastentries == -1) || (dp->entries > lastentries)) { |
44a7a5ab A |
546 | lastentries = dp->entries; |
547 | if ((array = | |
548 | realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { | |
6c780a1f | 549 | warn(NULL); |
44a7a5ab A |
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 | ||
44a7a5ab A |
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 | ||
6c780a1f | 565 | colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); |
44a7a5ab A |
566 | if (termwidth < 2 * colwidth) { |
567 | printscol(dp); | |
568 | return; | |
569 | } | |
44a7a5ab | 570 | numcols = termwidth / colwidth; |
6c780a1f A |
571 | numrows = num / numcols; |
572 | if (num % numcols) | |
573 | ++numrows; | |
44a7a5ab | 574 | |
4d0bb651 | 575 | assert(dp->list); |
44a7a5ab | 576 | if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) |
6c780a1f A |
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; | |
44a7a5ab | 601 | } |
6c780a1f | 602 | (void)putchar('\n'); |
44a7a5ab | 603 | } |
44a7a5ab A |
604 | } |
605 | ||
606 | /* | |
607 | * print [inode] [size] name | |
608 | * return # of characters printed, no trailing characters. | |
609 | */ | |
610 | static int | |
6c780a1f | 611 | printaname(FTSENT *p, u_long inodefield, u_long sizefield) |
44a7a5ab A |
612 | { |
613 | struct stat *sp; | |
614 | int chcnt; | |
6c780a1f A |
615 | #ifdef COLORLS |
616 | int color_printed = 0; | |
617 | #endif | |
44a7a5ab A |
618 | |
619 | sp = p->fts_statp; | |
620 | chcnt = 0; | |
621 | if (f_inode) | |
40bf83fe A |
622 | #if _DARWIN_FEATURE_64_BIT_INODE |
623 | chcnt += printf("%*llu ", (int)inodefield, (u_quad_t)sp->st_ino); | |
624 | #else | |
6c780a1f | 625 | chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino); |
40bf83fe | 626 | #endif |
44a7a5ab | 627 | if (f_size) |
6c780a1f A |
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 | |
44a7a5ab A |
639 | if (f_type) |
640 | chcnt += printtype(sp->st_mode); | |
641 | return (chcnt); | |
642 | } | |
643 | ||
644 | static void | |
6c780a1f | 645 | printtime(time_t ftime) |
44a7a5ab | 646 | { |
6c780a1f A |
647 | char longstring[80]; |
648 | static time_t now; | |
649 | const char *format; | |
650 | static int d_first = -1; | |
651 | ||
6c780a1f A |
652 | if (d_first < 0) |
653 | d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); | |
6c780a1f A |
654 | if (now == 0) |
655 | now = time(NULL); | |
656 | ||
657 | #define SIXMONTHS ((365 / 2) * 86400) | |
44a7a5ab | 658 | if (f_sectime) |
6c780a1f A |
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 "; | |
864a4b6e A |
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 | } | |
6c780a1f A |
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); | |
44a7a5ab A |
677 | } |
678 | ||
679 | static int | |
6c780a1f | 680 | printtype(u_int mode) |
44a7a5ab | 681 | { |
6c780a1f A |
682 | |
683 | if (f_slash) { | |
684 | if ((mode & S_IFMT) == S_IFDIR) { | |
685 | (void)putchar('/'); | |
686 | return (1); | |
687 | } | |
688 | return (0); | |
689 | } | |
690 | ||
44a7a5ab A |
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); | |
6c780a1f A |
707 | default: |
708 | break; | |
44a7a5ab A |
709 | } |
710 | if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { | |
711 | (void)putchar('*'); | |
712 | return (1); | |
713 | } | |
714 | return (0); | |
715 | } | |
716 | ||
6c780a1f A |
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 | ||
44a7a5ab | 734 | static void |
6c780a1f A |
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) | |
44a7a5ab A |
865 | { |
866 | int lnklen; | |
6c780a1f A |
867 | char name[MAXPATHLEN + 1]; |
868 | char path[MAXPATHLEN + 1]; | |
44a7a5ab A |
869 | |
870 | if (p->fts_level == FTS_ROOTLEVEL) | |
871 | (void)snprintf(name, sizeof(name), "%s", p->fts_name); | |
6c780a1f | 872 | else |
44a7a5ab A |
873 | (void)snprintf(name, sizeof(name), |
874 | "%s/%s", p->fts_parent->fts_accpath, p->fts_name); | |
44a7a5ab A |
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'; | |
6c780a1f A |
880 | (void)printf(" -> "); |
881 | (void)printname(path); | |
882 | } | |
883 | ||
884 | static void | |
885 | printsize(size_t width, off_t bytes) | |
886 | { | |
6c780a1f | 887 | |
864a4b6e A |
888 | if (f_humanval) { |
889 | char buf[5]; | |
6c780a1f | 890 | |
864a4b6e A |
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 | |
40bf83fe | 895 | (void)printf("%*jd ", (u_int)width, (intmax_t)bytes); |
44a7a5ab | 896 | } |