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