Print this page
5613 odd ls -U behaviour if output is not a terminal
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/ls/ls.c
+++ new/usr/src/cmd/ls/ls.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
↓ open down ↓ |
14 lines elided |
↑ open up ↑ |
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
25 + * Copyright 2015 Gary Mills
25 26 */
26 27
27 28 /*
28 29 * Copyright 2009 Jason King. All rights reserved.
29 30 * Use is subject to license terms.
30 31 */
31 32
32 33 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
33 34 /* All Rights Reserved */
34 35
35 36 /* Copyright (c) 1987, 1988 Microsoft Corporation */
36 37 /* All Rights Reserved */
37 38
38 39 /*
39 40 * List files or directories
40 41 */
41 42
42 43 #include <sys/param.h>
43 44 #include <sys/types.h>
44 45 #include <sys/mkdev.h>
45 46 #include <sys/stat.h>
46 47 #include <sys/acl.h>
47 48
48 49 #include <wchar.h>
49 50 #include <stdio.h>
50 51 #include <ctype.h>
51 52 #include <dirent.h>
52 53 #include <string.h>
53 54 #include <locale.h>
54 55 #include <curses.h>
55 56 #include <term.h>
56 57 #include <termios.h>
57 58 #include <stdlib.h>
58 59 #include <widec.h>
59 60 #include <locale.h>
60 61 #include <wctype.h>
61 62 #include <pwd.h>
62 63 #include <grp.h>
63 64 #include <limits.h>
64 65 #include <fcntl.h>
65 66 #include <unistd.h>
66 67 #include <libgen.h>
67 68 #include <errno.h>
68 69 #include <aclutils.h>
69 70 #include <libnvpair.h>
70 71 #include <libcmdutils.h>
71 72 #include <attr.h>
72 73 #include <getopt.h>
73 74 #include <inttypes.h>
74 75
75 76 #ifndef STANDALONE
76 77 #define TERMINFO
77 78 #endif
78 79
79 80 /*
80 81 * -DNOTERMINFO can be defined on the cc command line to prevent
81 82 * the use of terminfo. This should be done on systems not having
82 83 * the terminfo feature(pre 6.0 systems ?).
83 84 * As a result, columnar listings assume 80 columns for output,
84 85 * unless told otherwise via the COLUMNS environment variable.
85 86 */
86 87 #ifdef NOTERMINFO
87 88 #undef TERMINFO
88 89 #endif
89 90
90 91 #include <term.h>
91 92
92 93 #define BFSIZE 16
93 94 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
94 95 #define ISARG 0100000
95 96
96 97 /*
97 98 * this flag has been added to manipulate the display of S instead of 'l' when
98 99 * the file is not a regular file and when group execution bit is off
99 100 */
100 101 #define LS_NOTREG 010000
101 102
102 103
103 104 /*
104 105 * Date and time formats
105 106 *
106 107 * b --- abbreviated month name
107 108 * e --- day number
108 109 * Y --- year in the form ccyy
109 110 * H --- hour(24-hour version)
110 111 * M --- minute
111 112 * F --- yyyy-mm-dd
112 113 * T --- hh:mm:ss
113 114 * z --- time zone as hours displacement from UTC
114 115 * note that %F and %z are from the ISO C99 standard and are
115 116 * not present in older C libraries
116 117 */
117 118 #define FORMAT_OLD " %b %e %Y "
118 119 #define FORMAT_NEW " %b %e %H:%M "
119 120 #define FORMAT_LONG " %b %e %T %Y "
120 121 #define FORMAT_ISO_FULL " %%F %%T.%.09ld %%z "
121 122 #define FORMAT_ISO_LONG " %F %R "
122 123 #define FORMAT_ISO_NEW " %m-%d %H:%M "
123 124 #define FORMAT_ISO_OLD " %F "
124 125
125 126 #undef BUFSIZ
126 127 #define BUFSIZ 4096
127 128 #define NUMBER_WIDTH 40
128 129 #define FMTSIZE 50
129 130
130 131 struct ditem {
131 132 dev_t dev; /* directory items device number */
132 133 ino_t ino; /* directory items inode number */
133 134 struct ditem *parent; /* dir items ptr to its parent's info */
134 135 };
135 136 /* Holds boolean extended system attributes */
136 137 struct attrb {
137 138 char *name;
138 139 };
139 140 /* Holds timestamp extended system attributes */
140 141 struct attrtm {
141 142 char *name;
142 143 uint64_t stm;
143 144 uint64_t nstm;
144 145 };
145 146
146 147 #define LSA_NONE (0)
147 148 #define LSA_BOLD (1L << 0)
148 149 #define LSA_UNDERSCORE (1L << 1)
149 150 #define LSA_BLINK (1L << 2)
150 151 #define LSA_REVERSE (1L << 3)
151 152 #define LSA_CONCEALED (1L << 4)
152 153
153 154 /* these should be ordered most general to most specific */
154 155 typedef enum LS_CFTYPE {
155 156 LS_NORMAL,
156 157 LS_FILE,
157 158 LS_EXEC,
158 159 LS_DIR,
159 160 LS_LINK,
160 161 LS_FIFO,
161 162 LS_SOCK,
162 163 LS_DOOR,
163 164 LS_BLK,
164 165 LS_CHR,
165 166 LS_PORT,
166 167 LS_STICKY,
167 168 LS_ORPHAN,
168 169 LS_SETGID,
169 170 LS_SETUID,
170 171 LS_OTHER_WRITABLE,
171 172 LS_STICKY_OTHER_WRITABLE,
172 173 LS_PAT
173 174 } ls_cftype_t;
174 175
175 176 typedef struct {
176 177 char *sfx;
177 178 ls_cftype_t ftype;
178 179 int attr;
179 180 int fg;
180 181 int bg;
181 182 } ls_color_t;
182 183
183 184 struct lbuf {
184 185 union {
185 186 char lname[MAXNAMLEN]; /* used for filename in a directory */
186 187 char *namep; /* for name in ls-command; */
187 188 } ln;
188 189 char ltype; /* filetype */
189 190 ino_t lnum; /* inode number of file */
190 191 mode_t lflags; /* 0777 bits used as r,w,x permissions */
191 192 nlink_t lnl; /* number of links to file */
192 193 uid_t luid;
193 194 gid_t lgid;
194 195 off_t lsize; /* filesize or major/minor dev numbers */
195 196 blkcnt_t lblocks; /* number of file blocks */
196 197 timestruc_t lmtime;
197 198 timestruc_t lat;
198 199 timestruc_t lct;
199 200 timestruc_t lmt;
200 201 char *flinkto; /* symbolic link contents */
201 202 char acl; /* indicate there are additional acl entries */
202 203 int cycle; /* cycle detected flag */
203 204 struct ditem *ancinfo; /* maintains ancestor info */
204 205 acl_t *aclp; /* ACL if present */
205 206 struct attrb *exttr; /* boolean extended system attributes */
206 207 struct attrtm *extm; /* timestamp extended system attributes */
207 208 ls_color_t *color; /* color for entry */
208 209 ls_color_t *link_color; /* color for symlink */
209 210 };
210 211
211 212 struct dchain {
212 213 char *dc_name; /* path name */
213 214 int cycle_detected; /* cycle detected visiting this directory */
214 215 struct ditem *myancinfo; /* this directory's ancestry info */
215 216 struct dchain *dc_next; /* next directory in the chain */
216 217 };
217 218
218 219 /*
219 220 * A numbuf_t is used when converting a number to a string representation
220 221 */
221 222 typedef char numbuf_t[NUMBER_WIDTH];
222 223
223 224 static struct dchain *dfirst; /* start of the dir chain */
224 225 static struct dchain *cdfirst; /* start of the current dir chain */
225 226 static struct dchain *dtemp; /* temporary - used for linking */
226 227 static char *curdir; /* the current directory */
227 228
228 229 static int first = 1; /* true if first line is not yet printed */
229 230 static int nfiles = 0; /* number of flist entries in current use */
230 231 static int nargs = 0; /* number of flist entries used for arguments */
231 232 static int maxfils = 0; /* number of flist/lbuf entries allocated */
232 233 static int maxn = 0; /* number of flist entries with lbufs asigned */
233 234 static int quantn = 64; /* allocation growth quantum */
234 235
235 236 static struct lbuf *nxtlbf; /* ptr to next lbuf to be assigned */
236 237 static struct lbuf **flist; /* ptr to list of lbuf pointers */
237 238 static struct lbuf *gstat(char *, int, struct ditem *);
238 239 static char *getname(uid_t);
239 240 static char *getgroup(gid_t);
240 241 static char *makename(char *, char *);
241 242 static void pentry(struct lbuf *);
242 243 static void column(void);
243 244 static void pmode(mode_t aflag);
244 245 static void selection(int *);
245 246 static void new_line(void);
246 247 static void rddir(char *, struct ditem *);
247 248 static int strcol(unsigned char *);
248 249 static void pem(struct lbuf **, struct lbuf **, int);
249 250 static void pdirectory(char *, int, int, int, struct ditem *);
250 251 static struct cachenode *findincache(struct cachenode **, long);
251 252 static void csi_pprintf(unsigned char *);
252 253 static void pprintf(char *, char *);
253 254 static int compar(struct lbuf **pp1, struct lbuf **pp2);
254 255 static char *number_to_scaled_string(numbuf_t buf,
255 256 unsigned long long number,
256 257 long scale);
257 258 static void record_ancestry(char *, struct stat *, struct lbuf *,
258 259 int, struct ditem *);
259 260 static void ls_color_init(void);
260 261 static ls_color_t *ls_color_find(const char *, mode_t);
261 262 static void ls_start_color(ls_color_t *);
262 263 static void ls_end_color(void);
263 264
264 265 static int aflg;
265 266 static int atflg;
266 267 static int bflg;
267 268 static int cflg;
268 269 static int dflg;
269 270 static int eflg;
270 271 static int fflg;
271 272 static int gflg;
272 273 static int hflg;
273 274 static int iflg;
274 275 static int lflg;
275 276 static int mflg;
276 277 static int nflg;
277 278 static int oflg;
278 279 static int pflg;
279 280 static int qflg;
280 281 static int rflg = 1; /* init to 1 for special use in compar */
281 282 static int sflg;
282 283 static int tflg;
283 284 static int uflg;
284 285 static int Uflg;
285 286 static int wflg;
286 287 static int xflg;
287 288 static int Aflg;
288 289 static int Bflg;
289 290 static int Cflg;
290 291 static int Eflg;
291 292 static int Fflg;
292 293 static int Hflg;
293 294 static int Lflg;
294 295 static int Rflg;
295 296 static int Sflg;
296 297 static int vflg;
297 298 static int Vflg;
298 299 static int saflg; /* boolean extended system attr. */
299 300 static int sacnt; /* number of extended system attr. */
300 301 static int copt;
301 302 static int vopt;
302 303 static int tmflg; /* create time ext. system attr. */
303 304 static int ctm;
304 305 static int atm;
305 306 static int mtm;
306 307 static int crtm;
307 308 static int alltm;
308 309 static long hscale;
309 310 static mode_t flags;
310 311 static int err = 0; /* Contains return code */
311 312 static int colorflg;
312 313 static int file_typeflg;
313 314 static int noflist = 0;
314 315
315 316 static uid_t lastuid = (uid_t)-1;
316 317 static gid_t lastgid = (gid_t)-1;
317 318 static char *lastuname = NULL;
318 319 static char *lastgname = NULL;
319 320
320 321 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
321 322 static int statreq;
322 323
323 324 static uint64_t block_size = 1;
324 325 static char *dotp = ".";
325 326
326 327 static u_longlong_t tblocks; /* number of blocks of files in a directory */
327 328 static time_t year, now;
328 329
329 330 static int num_cols = 80;
330 331 static int colwidth;
331 332 static int filewidth;
332 333 static int fixedwidth;
333 334 static int nomocore;
334 335 static int curcol;
335 336
336 337 static struct winsize win;
337 338
338 339 /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
339 340 static const char *time_fmt_old = FORMAT_OLD; /* non-recent files */
340 341 static const char *time_fmt_new = FORMAT_NEW; /* recent files */
341 342 static int time_custom; /* != 0 if a custom format */
342 343 static char time_buf[FMTSIZE]; /* array to hold day and time */
343 344
344 345 static int lsc_debug;
345 346 static ls_color_t *lsc_match;
346 347 static ls_color_t *lsc_colors;
347 348 static size_t lsc_ncolors;
348 349 static char *lsc_bold;
349 350 static char *lsc_underline;
350 351 static char *lsc_blink;
351 352 static char *lsc_reverse;
352 353 static char *lsc_concealed;
353 354 static char *lsc_none;
354 355 static char *lsc_setfg;
355 356 static char *lsc_setbg;
356 357 static ls_color_t *lsc_orphan;
357 358
358 359 #define NOTWORKINGDIR(d, l) (((l) < 2) || \
359 360 (strcmp((d) + (l) - 2, "/.") != 0))
360 361
361 362 #define NOTPARENTDIR(d, l) (((l) < 3) || \
362 363 (strcmp((d) + (l) - 3, "/..") != 0))
363 364 /* Extended system attributes support */
364 365 static int get_sysxattr(char *, struct lbuf *);
365 366 static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
366 367 static void set_sysattrtm_display(char *, struct lbuf *);
367 368 static void format_time(time_t, time_t);
368 369 static void print_time(struct lbuf *);
369 370 static void format_attrtime(struct lbuf *);
370 371 static void *xmalloc(size_t, struct lbuf *);
371 372 static void free_sysattr(struct lbuf *);
372 373 static nvpair_t *pair;
373 374 static nvlist_t *response;
374 375 static int acl_err;
375 376
376 377 const struct option long_options[] = {
377 378 { "all", no_argument, NULL, 'a' },
378 379 { "almost-all", no_argument, NULL, 'A' },
379 380 { "escape", no_argument, NULL, 'b' },
380 381 { "classify", no_argument, NULL, 'F' },
381 382 { "human-readable", no_argument, NULL, 'h' },
382 383 { "dereference", no_argument, NULL, 'L' },
383 384 { "dereference-command-line", no_argument, NULL, 'H' },
384 385 { "ignore-backups", no_argument, NULL, 'B' },
385 386 { "inode", no_argument, NULL, 'i' },
386 387 { "numeric-uid-gid", no_argument, NULL, 'n' },
387 388 { "no-group", no_argument, NULL, 'o' },
388 389 { "hide-control-chars", no_argument, NULL, 'q' },
389 390 { "reverse", no_argument, NULL, 'r' },
390 391 { "recursive", no_argument, NULL, 'R' },
391 392 { "size", no_argument, NULL, 's' },
392 393 { "width", required_argument, NULL, 'w' },
393 394
394 395 /* no short options for these */
395 396 { "block-size", required_argument, NULL, 0 },
396 397 { "full-time", no_argument, NULL, 0 },
397 398 { "si", no_argument, NULL, 0 },
398 399 { "color", optional_argument, NULL, 0 },
399 400 { "colour", optional_argument, NULL, 0},
400 401 { "file-type", no_argument, NULL, 0 },
401 402 { "time-style", required_argument, NULL, 0 },
402 403
403 404 {0, 0, 0, 0}
404 405 };
405 406
406 407 int
407 408 main(int argc, char *argv[])
408 409 {
409 410 int c;
410 411 int i;
411 412 int width;
412 413 int amino = 0;
413 414 int opterr = 0;
414 415 int option_index = 0;
415 416 struct lbuf *ep;
416 417 struct lbuf lb;
417 418 struct ditem *myinfo = NULL;
418 419
419 420 (void) setlocale(LC_ALL, "");
420 421 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
421 422 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
422 423 #endif
423 424 (void) textdomain(TEXT_DOMAIN);
424 425 #ifdef STANDALONE
425 426 if (argv[0][0] == '\0')
426 427 argc = getargv("ls", &argv, 0);
427 428 #endif
428 429
429 430 lb.lmtime.tv_sec = time(NULL);
430 431 lb.lmtime.tv_nsec = 0;
431 432 year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
432 433 now = lb.lmtime.tv_sec + 60;
433 434 if (isatty(1)) {
434 435 Cflg = 1;
435 436 mflg = 0;
436 437 }
437 438
438 439 while ((c = getopt_long(argc, argv,
439 440 "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options,
440 441 &option_index)) != -1)
441 442 switch (c) {
442 443 case 0:
443 444 /* non-short options */
444 445 if (strcmp(long_options[option_index].name,
445 446 "color") == 0 ||
446 447 strcmp(long_options[option_index].name,
447 448 "colour") == 0) {
448 449 if (optarg == NULL ||
449 450 strcmp(optarg, "always") == 0 ||
450 451 strcmp(optarg, "yes") == 0 ||
451 452 strcmp(optarg, "force") == 0) {
452 453 colorflg++;
453 454 statreq++;
454 455 continue;
455 456 }
456 457
457 458 if (strcmp(optarg, "auto") == 0 ||
458 459 strcmp(optarg, "tty") == 0 ||
459 460 strcmp(optarg, "if-tty") == 0) {
460 461 if (isatty(1) == 1) {
461 462 colorflg++;
462 463 statreq++;
463 464 }
464 465 continue;
465 466 }
466 467
467 468 if (strcmp(optarg, "never") == 0 ||
468 469 strcmp(optarg, "no") == 0 ||
469 470 strcmp(optarg, "none") == 0) {
470 471 colorflg = 0;
471 472 continue;
472 473 }
473 474 (void) fprintf(stderr,
474 475 gettext("Invalid argument '%s' for "
475 476 "--color\n"), optarg);
476 477 ++opterr;
477 478 continue;
478 479 }
479 480
480 481 if (strcmp(long_options[option_index].name,
481 482 "si") == 0) {
482 483 hflg++;
483 484 hscale = 1000;
484 485 continue;
485 486 }
486 487
487 488 if (strcmp(long_options[option_index].name,
488 489 "block-size") == 0) {
489 490 size_t scale_len = strlen(optarg);
490 491 uint64_t scale = 1;
491 492 uint64_t kilo = 1024;
492 493 char scale_c;
493 494
494 495 if (scale_len == 0) {
495 496 (void) fprintf(stderr, gettext(
496 497 "Invalid block size \'%s\'\n"),
497 498 optarg);
498 499 exit(1);
499 500 }
500 501
501 502 scale_c = optarg[scale_len - 1];
502 503 if (scale_c == 'B') {
503 504 /* need at least digit, scale, B */
504 505 if (scale_len < 3) {
505 506 (void) fprintf(stderr, gettext(
506 507 "Invalid block size "
507 508 "\'%s\'\n"), optarg);
508 509 exit(1);
509 510 }
510 511 kilo = 1000;
511 512 scale_c = optarg[scale_len - 2];
512 513 if (isdigit(scale_c)) {
513 514 (void) fprintf(stderr,
514 515 gettext("Invalid block size"
515 516 " \'%s\'\n"), optarg);
516 517 exit(1);
517 518 }
518 519 /*
519 520 * make optarg[scale_len - 1] point to
520 521 * the scale factor
521 522 */
522 523 --scale_len;
523 524 }
524 525
525 526 switch (scale_c) {
526 527 case 'y':
527 528 case 'Y':
528 529 scale *= kilo;
529 530 /*FALLTHROUGH*/
530 531 case 'Z':
531 532 case 'z':
532 533 scale *= kilo;
533 534 /*FALLTHROUGH*/
534 535 case 'E':
535 536 case 'e':
536 537 scale *= kilo;
537 538 /*FALLTHROUGH*/
538 539 case 'P':
539 540 case 'p':
540 541 scale *= kilo;
541 542 /*FALLTHROUGH*/
542 543 case 'T':
543 544 case 't':
544 545 scale *= kilo;
545 546 /*FALLTHROUGH*/
546 547 case 'G':
547 548 case 'g':
548 549 scale *= kilo;
549 550 /*FALLTHROUGH*/
550 551 case 'M':
551 552 case 'm':
552 553 scale *= kilo;
553 554 /*FALLTHROUGH*/
554 555 case 'K':
555 556 case 'k':
556 557 scale *= kilo;
557 558 break;
558 559 default:
559 560 if (!isdigit(scale_c)) {
560 561 (void) fprintf(stderr,
561 562 gettext("Invalid character "
562 563 "following block size in "
563 564 "\'%s\'\n"), optarg);
564 565 exit(1);
565 566 }
566 567 }
567 568
568 569 /* NULL out scale constant if present */
569 570 if (scale > 1 && !isdigit(scale_c))
570 571 optarg[scale_len - 1] = '\0';
571 572
572 573 /* Based on testing, this is what GNU ls does */
573 574 block_size = strtoll(optarg, NULL, 0) * scale;
574 575 if (block_size < 1) {
575 576 (void) fprintf(stderr,
576 577 gettext("Invalid block size "
577 578 "\'%s\'\n"), optarg);
578 579 exit(1);
579 580 }
580 581 continue;
581 582 }
582 583
583 584 if (strcmp(long_options[option_index].name,
584 585 "file-type") == 0) {
585 586 file_typeflg++;
586 587 Fflg++;
587 588 statreq++;
588 589 continue;
589 590 }
590 591
591 592
592 593 if (strcmp(long_options[option_index].name,
593 594 "full-time") == 0) {
594 595 Eflg++;
595 596 statreq++;
596 597 eflg = 0;
597 598 time_fmt_old = FORMAT_ISO_FULL;
598 599 time_fmt_new = FORMAT_ISO_FULL;
599 600 continue;
600 601 }
601 602
602 603 if (strcmp(long_options[option_index].name,
603 604 "time-style") == 0) {
604 605 /* like -E, but doesn't imply -l */
605 606 if (strcmp(optarg, "full-iso") == 0) {
606 607 Eflg++;
607 608 statreq++;
608 609 eflg = 0;
609 610 time_fmt_old = FORMAT_ISO_FULL;
610 611 time_fmt_new = FORMAT_ISO_FULL;
611 612 continue;
612 613 }
613 614 if (strcmp(optarg, "long-iso") == 0) {
614 615 statreq++;
615 616 Eflg = 0;
616 617 eflg = 0;
617 618 time_fmt_old = FORMAT_ISO_LONG;
618 619 time_fmt_new = FORMAT_ISO_LONG;
619 620 continue;
620 621 }
621 622 if (strcmp(optarg, "iso") == 0) {
622 623 statreq++;
623 624 Eflg = 0;
624 625 eflg = 0;
625 626 time_fmt_old = FORMAT_ISO_OLD;
626 627 time_fmt_new = FORMAT_ISO_NEW;
627 628 continue;
628 629 }
629 630 /* should be the default */
630 631 if (strcmp(optarg, "locale") == 0) {
631 632 time_fmt_old = FORMAT_OLD;
632 633 time_fmt_new = FORMAT_NEW;
633 634 continue;
634 635 }
635 636 if (optarg[0] == '+') {
636 637 char *told, *tnew;
637 638 char *p;
638 639 size_t timelen = strlen(optarg);
639 640
640 641 p = strchr(optarg, '\n');
641 642 if (p != NULL)
642 643 *p++ = '\0';
643 644
644 645 /*
645 646 * Time format requires a leading and
646 647 * trailing space
647 648 * Add room for 3 spaces + 2 nulls
648 649 * The + in optarg is replaced with
649 650 * a space.
650 651 */
651 652 timelen += 2 + 3;
652 653 told = malloc(timelen);
653 654 if (told == NULL) {
654 655 perror("ls");
655 656 exit(2);
656 657 }
657 658
658 659 (void) memset(told, 0, timelen);
659 660 told[0] = ' ';
660 661 (void) strlcat(told, &optarg[1],
661 662 timelen);
662 663 (void) strlcat(told, " ", timelen);
663 664
664 665 if (p != NULL) {
665 666 size_t tnew_len;
666 667
667 668 tnew = told + strlen(told) + 1;
668 669 tnew_len = timelen -
669 670 strlen(told) - 1;
670 671
671 672 tnew[0] = ' ';
672 673 (void) strlcat(tnew, p,
673 674 tnew_len);
674 675 (void) strlcat(tnew, " ",
675 676 tnew_len);
676 677 time_fmt_new =
677 678 (const char *)tnew;
678 679 } else {
679 680 time_fmt_new =
680 681 (const char *)told;
681 682 }
682 683
683 684 time_fmt_old = (const char *)told;
684 685 time_custom = 1;
685 686 continue;
686 687 }
687 688 continue;
688 689 }
689 690
690 691 continue;
691 692
692 693 case 'a':
693 694 aflg++;
694 695 continue;
695 696 case 'A':
696 697 Aflg++;
697 698 continue;
698 699 case 'b':
699 700 bflg = 1;
700 701 qflg = 0;
701 702 continue;
702 703 case 'B':
703 704 Bflg = 1;
704 705 continue;
705 706 case 'c':
706 707 uflg = 0;
707 708 atm = 0;
708 709 ctm = 0;
709 710 mtm = 0;
710 711 crtm = 0;
711 712 cflg++;
712 713 continue;
713 714 case 'C':
714 715 Cflg = 1;
715 716 mflg = 0;
716 717 #ifdef XPG4
717 718 lflg = 0;
718 719 #endif
719 720 continue;
720 721 case 'd':
721 722 dflg++;
722 723 continue;
723 724 case 'e':
724 725 eflg++;
725 726 lflg++;
726 727 statreq++;
727 728 Eflg = 0;
728 729 time_fmt_old = FORMAT_LONG;
729 730 time_fmt_new = FORMAT_LONG;
730 731 continue;
731 732 case 'E':
732 733 Eflg++;
733 734 lflg++;
734 735 statreq++;
735 736 eflg = 0;
736 737 time_fmt_old = FORMAT_ISO_FULL;
737 738 time_fmt_new = FORMAT_ISO_FULL;
738 739 continue;
739 740 case 'f':
740 741 fflg++;
741 742 continue;
742 743 case 'F':
743 744 Fflg++;
744 745 statreq++;
745 746 continue;
746 747 case 'g':
747 748 gflg++;
748 749 lflg++;
749 750 statreq++;
750 751 continue;
751 752 case 'h':
752 753 hflg++;
753 754 hscale = 1024;
754 755 continue;
755 756 case 'H':
756 757 Hflg++;
757 758 /* -H and -L are mutually exclusive */
758 759 Lflg = 0;
759 760 continue;
760 761 case 'i':
761 762 iflg++;
762 763 continue;
763 764 case 'k':
764 765 block_size = 1024;
765 766 continue;
766 767 case 'l':
767 768 lflg++;
768 769 statreq++;
769 770 Cflg = 0;
770 771 xflg = 0;
771 772 mflg = 0;
772 773 atflg = 0;
773 774 continue;
774 775 case 'L':
775 776 Lflg++;
776 777 /* -H and -L are mutually exclusive */
777 778 Hflg = 0;
778 779 continue;
779 780 case 'm':
780 781 Cflg = 0;
781 782 mflg = 1;
782 783 #ifdef XPG4
783 784 lflg = 0;
784 785 #endif
785 786 continue;
786 787 case 'n':
787 788 nflg++;
788 789 lflg++;
789 790 statreq++;
790 791 Cflg = 0;
791 792 xflg = 0;
792 793 mflg = 0;
793 794 atflg = 0;
794 795 continue;
795 796 case 'o':
796 797 oflg++;
797 798 lflg++;
798 799 statreq++;
799 800 continue;
800 801 case 'p':
801 802 pflg++;
802 803 statreq++;
803 804 continue;
804 805 case 'q':
805 806 qflg = 1;
806 807 bflg = 0;
807 808 continue;
808 809 case 'r':
809 810 rflg = -1;
810 811 continue;
811 812 case 'R':
812 813 Rflg++;
813 814 statreq++;
814 815 continue;
815 816 case 's':
816 817 sflg++;
817 818 statreq++;
818 819 continue;
819 820 case 'S':
820 821 tflg = 0;
821 822 Uflg = 0;
822 823 Sflg++;
823 824 statreq++;
824 825 continue;
825 826 case 't':
826 827 Sflg = 0;
827 828 Uflg = 0;
828 829 tflg++;
829 830 statreq++;
830 831 continue;
831 832 case 'U':
832 833 Sflg = 0;
833 834 tflg = 0;
834 835 Uflg++;
835 836 continue;
836 837 case 'u':
837 838 cflg = 0;
838 839 atm = 0;
839 840 ctm = 0;
840 841 mtm = 0;
841 842 crtm = 0;
842 843 uflg++;
843 844 continue;
844 845 case 'V':
845 846 Vflg++;
846 847 /*FALLTHROUGH*/
847 848 case 'v':
848 849 vflg++;
849 850 #if !defined(XPG4)
850 851 if (lflg)
851 852 continue;
852 853 #endif
853 854 lflg++;
854 855 statreq++;
855 856 Cflg = 0;
856 857 xflg = 0;
857 858 mflg = 0;
858 859 continue;
859 860 case 'w':
860 861 wflg++;
861 862 num_cols = atoi(optarg);
862 863 continue;
863 864 case 'x':
864 865 xflg = 1;
865 866 Cflg = 1;
866 867 mflg = 0;
867 868 #ifdef XPG4
868 869 lflg = 0;
869 870 #endif
870 871 continue;
871 872 case '1':
872 873 Cflg = 0;
873 874 continue;
874 875 case '@':
875 876 #if !defined(XPG4)
876 877 /*
877 878 * -l has precedence over -@
878 879 */
879 880 if (lflg)
880 881 continue;
881 882 #endif
882 883 atflg++;
883 884 lflg++;
884 885 statreq++;
885 886 Cflg = 0;
886 887 xflg = 0;
887 888 mflg = 0;
888 889 continue;
889 890 case '/':
890 891 saflg++;
891 892 if (optarg != NULL) {
892 893 if (strcmp(optarg, "c") == 0) {
893 894 copt++;
894 895 vopt = 0;
895 896 } else if (strcmp(optarg, "v") == 0) {
896 897 vopt++;
897 898 copt = 0;
898 899 } else
899 900 opterr++;
900 901 } else
901 902 opterr++;
902 903 lflg++;
903 904 statreq++;
904 905 Cflg = 0;
905 906 xflg = 0;
906 907 mflg = 0;
907 908 continue;
908 909 case '%':
909 910 tmflg++;
910 911 if (optarg != NULL) {
911 912 if (strcmp(optarg, "ctime") == 0) {
912 913 ctm++;
913 914 atm = 0;
914 915 mtm = 0;
915 916 crtm = 0;
916 917 } else if (strcmp(optarg, "atime") == 0) {
917 918 atm++;
918 919 ctm = 0;
919 920 mtm = 0;
920 921 crtm = 0;
921 922 uflg = 0;
922 923 cflg = 0;
923 924 } else if (strcmp(optarg, "mtime") == 0) {
924 925 mtm++;
925 926 atm = 0;
926 927 ctm = 0;
927 928 crtm = 0;
928 929 uflg = 0;
929 930 cflg = 0;
930 931 } else if (strcmp(optarg, "crtime") == 0) {
931 932 crtm++;
932 933 atm = 0;
933 934 ctm = 0;
934 935 mtm = 0;
935 936 uflg = 0;
936 937 cflg = 0;
937 938 } else if (strcmp(optarg, "all") == 0) {
938 939 alltm++;
939 940 atm = 0;
940 941 ctm = 0;
941 942 mtm = 0;
942 943 crtm = 0;
943 944 } else
944 945 opterr++;
945 946 } else
946 947 opterr++;
947 948
948 949 Sflg = 0;
949 950 statreq++;
950 951 mflg = 0;
951 952 continue;
952 953 case '?':
953 954 opterr++;
954 955 continue;
955 956 }
956 957
957 958 if (opterr) {
958 959 (void) fprintf(stderr, gettext(
959 960 "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
960 961 "%%[atime | crtime | ctime | mtime | all]"
961 962 " [files]\n"));
962 963 exit(2);
963 964 }
964 965
965 966 if (fflg) {
966 967 aflg++;
967 968 lflg = 0;
968 969 sflg = 0;
969 970 tflg = 0;
970 971 Sflg = 0;
971 972 statreq = 0;
972 973 }
973 974
974 975 fixedwidth = 2;
975 976 if (pflg || Fflg)
976 977 fixedwidth++;
977 978 if (iflg)
978 979 fixedwidth += 11;
979 980 if (sflg)
980 981 fixedwidth += 5;
981 982
982 983 if (lflg) {
983 984 if (!gflg && !oflg)
984 985 gflg = oflg = 1;
985 986 else
986 987 if (gflg && oflg)
987 988 gflg = oflg = 0;
988 989 Cflg = mflg = 0;
989 990 }
990 991
991 992 if (!wflg && (Cflg || mflg)) {
992 993 char *clptr;
993 994 if ((clptr = getenv("COLUMNS")) != NULL)
994 995 num_cols = atoi(clptr);
995 996 #ifdef TERMINFO
996 997 else {
997 998 if (ioctl(1, TIOCGWINSZ, &win) != -1)
998 999 num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
999 1000 }
1000 1001 #endif
1001 1002 }
1002 1003
1003 1004 /*
1004 1005 * When certain options (-f, or -U and -1, and not -l, etc.) are
1005 1006 * specified, don't cache each dirent as it's read. This 'noflist'
1006 1007 * option is set when there's no need to cache those dirents; instead,
1007 1008 * print them out as they're read.
1008 1009 */
1009 1010 if ((Uflg || fflg) && !Cflg && !lflg && !iflg && statreq == 0)
1010 1011 noflist = 1;
1011 1012
1012 1013 if (num_cols < 20 || num_cols > 1000)
1013 1014 /* assume it is an error */
1014 1015 num_cols = 80;
1015 1016
1016 1017 /* allocate space for flist and the associated */
1017 1018 /* data structures (lbufs) */
1018 1019 maxfils = quantn;
1019 1020 if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
1020 1021 ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
1021 1022 perror("ls");
1022 1023 exit(2);
1023 1024 }
1024 1025 if ((amino = (argc-optind)) == 0) {
1025 1026 /*
1026 1027 * case when no names are given
1027 1028 * in ls-command and current
1028 1029 * directory is to be used
1029 1030 */
1030 1031 argv[optind] = dotp;
1031 1032 }
1032 1033
1033 1034 if (colorflg)
1034 1035 ls_color_init();
1035 1036
1036 1037 for (i = 0; i < (amino ? amino : 1); i++) {
1037 1038
1038 1039 /*
1039 1040 * If we are recursing, we need to make sure we don't
1040 1041 * get into an endless loop. To keep track of the inodes
1041 1042 * (actually, just the directories) visited, we
1042 1043 * maintain a directory ancestry list for a file
1043 1044 * hierarchy. As we go deeper into the hierarchy,
1044 1045 * a parent directory passes its directory list
1045 1046 * info (device id, inode number, and a pointer to
1046 1047 * its parent) to each of its children. As we
1047 1048 * process a child that is a directory, we save
1048 1049 * its own personal directory list info. We then
1049 1050 * check to see if the child has already been
1050 1051 * processed by comparing its device id and inode
1051 1052 * number from its own personal directory list info
1052 1053 * to that of each of its ancestors. If there is a
1053 1054 * match, then we know we've detected a cycle.
1054 1055 */
1055 1056 if (Rflg) {
1056 1057 /*
1057 1058 * This is the first parent in this lineage
1058 1059 * (first in a directory hierarchy), so
1059 1060 * this parent's parent doesn't exist. We
1060 1061 * only initialize myinfo when we are
1061 1062 * recursing, otherwise it's not used.
1062 1063 */
1063 1064 if ((myinfo = (struct ditem *)malloc(
1064 1065 sizeof (struct ditem))) == NULL) {
1065 1066 perror("ls");
1066 1067 exit(2);
1067 1068 } else {
1068 1069 myinfo->dev = 0;
1069 1070 myinfo->ino = 0;
1070 1071 myinfo->parent = NULL;
1071 1072 }
1072 1073 }
1073 1074
1074 1075 if (Cflg || mflg) {
1075 1076 width = strcol((unsigned char *)argv[optind]);
1076 1077 if (width > filewidth)
1077 1078 filewidth = width;
1078 1079 }
1079 1080 if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
1080 1081 1, myinfo)) == NULL) {
1081 1082 if (nomocore)
1082 1083 exit(2);
1083 1084 err = 2;
1084 1085 optind++;
1085 1086 continue;
1086 1087 }
1087 1088 ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
1088 1089 ep->lflags |= ISARG;
1089 1090 optind++;
1090 1091 nargs++; /* count good arguments stored in flist */
1091 1092 if (acl_err)
1092 1093 err = 2;
1093 1094 }
1094 1095 colwidth = fixedwidth + filewidth;
1095 1096 if (!Uflg)
1096 1097 qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
1097 1098 (int (*)(const void *, const void *))compar);
1098 1099 for (i = 0; i < nargs; i++) {
1099 1100 if ((flist[i]->ltype == 'd' && dflg == 0) || fflg)
1100 1101 break;
1101 1102 }
1102 1103
1103 1104 pem(&flist[0], &flist[i], 0);
1104 1105 for (; i < nargs; i++) {
1105 1106 pdirectory(flist[i]->ln.namep, Rflg ||
1106 1107 (amino > 1), nargs, 0, flist[i]->ancinfo);
1107 1108 if (nomocore)
1108 1109 exit(2);
1109 1110 /* -R: print subdirectories found */
1110 1111 while (dfirst || cdfirst) {
1111 1112 /* Place direct subdirs on front in right order */
1112 1113 while (cdfirst) {
1113 1114 /* reverse cdfirst onto front of dfirst */
1114 1115 dtemp = cdfirst;
1115 1116 cdfirst = cdfirst -> dc_next;
1116 1117 dtemp -> dc_next = dfirst;
1117 1118 dfirst = dtemp;
1118 1119 }
1119 1120 /* take off first dir on dfirst & print it */
1120 1121 dtemp = dfirst;
1121 1122 dfirst = dfirst->dc_next;
1122 1123 pdirectory(dtemp->dc_name, 1, nargs,
1123 1124 dtemp->cycle_detected, dtemp->myancinfo);
1124 1125 if (nomocore)
1125 1126 exit(2);
1126 1127 free(dtemp->dc_name);
1127 1128 free(dtemp);
1128 1129 }
1129 1130 }
1130 1131
1131 1132 return (err);
1132 1133 }
1133 1134
1134 1135 /*
1135 1136 * pdirectory: print the directory name, labelling it if title is
1136 1137 * nonzero, using lp as the place to start reading in the dir.
1137 1138 */
1138 1139 static void
1139 1140 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
1140 1141 {
1141 1142 struct dchain *dp;
1142 1143 struct lbuf *ap;
1143 1144 char *pname;
1144 1145 int j;
1145 1146
1146 1147 filewidth = 0;
1147 1148 curdir = name;
1148 1149 if (title) {
1149 1150 if (!first)
1150 1151 (void) putc('\n', stdout);
1151 1152 pprintf(name, ":");
1152 1153 new_line();
1153 1154 }
1154 1155 /*
1155 1156 * If there was a cycle detected, then notify and don't report
1156 1157 * further.
1157 1158 */
1158 1159 if (cdetect) {
1159 1160 if (lflg || sflg) {
1160 1161 curcol += printf(gettext("total %d"), 0);
1161 1162 new_line();
1162 1163 }
1163 1164 (void) fprintf(stderr, gettext(
1164 1165 "ls: cycle detected for %s\n"), name);
1165 1166 return;
1166 1167 }
1167 1168
1168 1169 nfiles = lp;
1169 1170 rddir(name, myinfo);
1170 1171 if (nomocore || noflist)
1171 1172 return;
1172 1173 if (fflg == 0 && Uflg == 0)
1173 1174 qsort(&flist[lp], (unsigned)(nfiles - lp),
1174 1175 sizeof (struct lbuf *),
1175 1176 (int (*)(const void *, const void *))compar);
1176 1177 if (Rflg) {
1177 1178 for (j = nfiles - 1; j >= lp; j--) {
1178 1179 ap = flist[j];
1179 1180 if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
1180 1181 strcmp(ap->ln.lname, "..")) {
1181 1182 dp = malloc(sizeof (struct dchain));
1182 1183 if (dp == NULL) {
1183 1184 perror("ls");
1184 1185 exit(2);
1185 1186 }
1186 1187 pname = makename(curdir, ap->ln.lname);
1187 1188 if ((dp->dc_name = strdup(pname)) == NULL) {
1188 1189 perror("ls");
1189 1190 exit(2);
1190 1191 }
1191 1192 dp->cycle_detected = ap->cycle;
1192 1193 dp->myancinfo = ap->ancinfo;
1193 1194 dp->dc_next = dfirst;
1194 1195 dfirst = dp;
1195 1196 }
1196 1197 }
1197 1198 }
1198 1199 if (lflg || sflg) {
1199 1200 curcol += printf(gettext("total %llu"), tblocks);
1200 1201 new_line();
1201 1202 }
1202 1203 pem(&flist[lp], &flist[nfiles], lflg||sflg);
1203 1204 }
1204 1205
1205 1206 /*
1206 1207 * pem: print 'em. Print a list of files (e.g. a directory) bounded
1207 1208 * by slp and lp.
1208 1209 */
1209 1210 static void
1210 1211 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
1211 1212 {
1212 1213 long row, nrows, i;
1213 1214 int col, ncols = 1;
1214 1215 struct lbuf **ep;
1215 1216
1216 1217 if (Cflg || mflg) {
1217 1218 if (colwidth <= num_cols) {
1218 1219 ncols = num_cols / colwidth;
1219 1220 }
1220 1221 }
1221 1222
1222 1223 if (ncols == 1 || mflg || xflg || !Cflg) {
1223 1224 for (ep = slp; ep < lp; ep++)
1224 1225 pentry(*ep);
1225 1226 new_line();
1226 1227 return;
1227 1228 }
1228 1229 /* otherwise print -C columns */
1229 1230 if (tot_flag) {
1230 1231 slp--;
1231 1232 row = 1;
1232 1233 }
1233 1234 else
1234 1235 row = 0;
1235 1236
1236 1237 nrows = (lp - slp - 1) / ncols + 1;
1237 1238 for (i = 0; i < nrows; i++, row++) {
1238 1239 for (col = 0; col < ncols; col++) {
1239 1240 ep = slp + (nrows * col) + row;
1240 1241 if (ep < lp)
1241 1242 pentry(*ep);
1242 1243 }
1243 1244 new_line();
1244 1245 }
1245 1246 }
1246 1247
1247 1248 /*
1248 1249 * print one output entry;
1249 1250 * if uid/gid is not found in the appropriate
1250 1251 * file(passwd/group), then print uid/gid instead of
1251 1252 * user/group name;
1252 1253 */
↓ open down ↓ |
1218 lines elided |
↑ open up ↑ |
1253 1254 static void
1254 1255 pentry(struct lbuf *ap)
1255 1256 {
1256 1257 struct lbuf *p;
1257 1258 numbuf_t hbuf;
1258 1259 char *dmark = ""; /* Used if -p or -F option active */
1259 1260 char *cp;
1260 1261 char *str;
1261 1262
1262 1263 if (noflist) {
1263 - (void) printf("%s\n", ap->ln.lname);
1264 + (void) printf("%s\n", (ap->lflags & ISARG) ? ap->ln.namep :
1265 + ap->ln.lname);
1264 1266 return;
1265 1267 }
1266 1268
1267 1269 p = ap;
1268 1270 column();
1269 1271 if (iflg) {
1270 1272 if (mflg && !lflg)
1271 1273 curcol += printf("%llu ", (long long)p->lnum);
1272 1274 else
1273 1275 curcol += printf("%10llu ", (long long)p->lnum);
1274 1276 }
1275 1277 if (sflg) {
1276 1278 curcol += printf((mflg && !lflg) ? "%lld " :
1277 1279 (p->lblocks < 10000) ? "%4lld " : "%lld ",
1278 1280 (p->ltype != 'b' && p->ltype != 'c') ?
1279 1281 p->lblocks : 0LL);
1280 1282 }
1281 1283 if (lflg) {
1282 1284 (void) putchar(p->ltype);
1283 1285 curcol++;
1284 1286 pmode(p->lflags);
1285 1287
1286 1288 /* ACL: additional access mode flag */
1287 1289 (void) putchar(p->acl);
1288 1290 curcol++;
1289 1291
1290 1292 curcol += printf("%3lu ", (ulong_t)p->lnl);
1291 1293 if (oflg) {
1292 1294 if (!nflg) {
1293 1295 cp = getname(p->luid);
1294 1296 curcol += printf("%-8s ", cp);
1295 1297 } else
1296 1298 curcol += printf("%-8lu ", (ulong_t)p->luid);
1297 1299 }
1298 1300 if (gflg) {
1299 1301 if (!nflg) {
1300 1302 cp = getgroup(p->lgid);
1301 1303 curcol += printf("%-8s ", cp);
1302 1304 } else
1303 1305 curcol += printf("%-8lu ", (ulong_t)p->lgid);
1304 1306 }
1305 1307 if (p->ltype == 'b' || p->ltype == 'c') {
1306 1308 curcol += printf("%3u, %2u",
1307 1309 (uint_t)major((dev_t)p->lsize),
1308 1310 (uint_t)minor((dev_t)p->lsize));
1309 1311 } else if (hflg && (p->lsize >= hscale)) {
1310 1312 curcol += printf("%7s",
1311 1313 number_to_scaled_string(hbuf, p->lsize, hscale));
1312 1314 } else {
1313 1315 uint64_t bsize = p->lsize / block_size;
1314 1316
1315 1317 /*
1316 1318 * Round up only when using blocks > 1 byte, otherwise
1317 1319 * 'normal' sizes display 1 byte too large.
1318 1320 */
1319 1321 if (p->lsize % block_size != 0)
1320 1322 bsize++;
1321 1323
1322 1324 curcol += printf("%7" PRIu64, bsize);
1323 1325 }
1324 1326 format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec);
1325 1327 /* format extended system attribute time */
1326 1328 if (tmflg && crtm)
1327 1329 format_attrtime(p);
1328 1330
1329 1331 curcol += printf("%s", time_buf);
1330 1332
1331 1333 }
1332 1334 /*
1333 1335 * prevent both "->" and trailing marks
1334 1336 * from appearing
1335 1337 */
1336 1338
1337 1339 if (pflg && p->ltype == 'd')
1338 1340 dmark = "/";
1339 1341
1340 1342 if (Fflg && !(lflg && p->flinkto)) {
1341 1343 if (p->ltype == 'd')
1342 1344 dmark = "/";
1343 1345 else if (p->ltype == 'D')
1344 1346 dmark = ">";
1345 1347 else if (p->ltype == 'p')
1346 1348 dmark = "|";
1347 1349 else if (p->ltype == 'l')
1348 1350 dmark = "@";
1349 1351 else if (p->ltype == 's')
1350 1352 dmark = "=";
1351 1353 else if (!file_typeflg &&
1352 1354 (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)))
1353 1355 dmark = "*";
1354 1356 else
1355 1357 dmark = "";
1356 1358 }
1357 1359
1358 1360 if (colorflg)
1359 1361 ls_start_color(p->color);
1360 1362
1361 1363 if (p->lflags & ISARG)
1362 1364 str = p->ln.namep;
1363 1365 else
1364 1366 str = p->ln.lname;
1365 1367
1366 1368 if (qflg || bflg) {
1367 1369 csi_pprintf((unsigned char *)str);
1368 1370
1369 1371 if (lflg && p->flinkto) {
1370 1372 if (colorflg)
1371 1373 ls_end_color();
1372 1374 csi_pprintf((unsigned char *)" -> ");
1373 1375 if (colorflg)
1374 1376 ls_start_color(p->link_color);
1375 1377 csi_pprintf((unsigned char *)p->flinkto);
1376 1378 } else {
1377 1379 csi_pprintf((unsigned char *)dmark);
1378 1380 }
1379 1381 } else {
1380 1382 (void) printf("%s", str);
1381 1383 curcol += strcol((unsigned char *)str);
1382 1384
1383 1385 if (lflg && p->flinkto) {
1384 1386 if (colorflg)
1385 1387 ls_end_color();
1386 1388 str = " -> ";
1387 1389 (void) printf("%s", str);
1388 1390 curcol += strcol((unsigned char *)str);
1389 1391 if (colorflg)
1390 1392 ls_start_color(p->link_color);
1391 1393 (void) printf("%s", p->flinkto);
1392 1394 curcol += strcol((unsigned char *)p->flinkto);
1393 1395 } else {
1394 1396 (void) printf("%s", dmark);
1395 1397 curcol += strcol((unsigned char *)dmark);
1396 1398 }
1397 1399 }
1398 1400
1399 1401 if (colorflg)
1400 1402 ls_end_color();
1401 1403
1402 1404 /* Display extended system attributes */
1403 1405 if (saflg) {
1404 1406 int i;
1405 1407
1406 1408 new_line();
1407 1409 (void) printf(" \t{");
1408 1410 if (p->exttr != NULL) {
1409 1411 int k = 0;
1410 1412 for (i = 0; i < sacnt; i++) {
1411 1413 if (p->exttr[i].name != NULL)
1412 1414 k++;
1413 1415 }
1414 1416 for (i = 0; i < sacnt; i++) {
1415 1417 if (p->exttr[i].name != NULL) {
1416 1418 (void) printf("%s", p->exttr[i].name);
1417 1419 k--;
1418 1420 if (vopt && (k != 0))
1419 1421 (void) printf(",");
1420 1422 }
1421 1423 }
1422 1424 }
1423 1425 (void) printf("}\n");
1424 1426 }
1425 1427 /* Display file timestamps and extended system attribute timestamps */
1426 1428 if (tmflg && alltm) {
1427 1429 new_line();
1428 1430 print_time(p);
1429 1431 new_line();
1430 1432 }
1431 1433 if (vflg) {
1432 1434 new_line();
1433 1435 if (p->aclp) {
1434 1436 acl_printacl(p->aclp, num_cols, Vflg);
1435 1437 }
1436 1438 }
1437 1439 /* Free extended system attribute lists */
1438 1440 if (saflg || tmflg)
1439 1441 free_sysattr(p);
1440 1442 }
1441 1443
1442 1444 /* print various r,w,x permissions */
1443 1445 static void
1444 1446 pmode(mode_t aflag)
1445 1447 {
1446 1448 /* these arrays are declared static to allow initializations */
1447 1449 static int m0[] = { 1, S_IRUSR, 'r', '-' };
1448 1450 static int m1[] = { 1, S_IWUSR, 'w', '-' };
1449 1451 static int m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
1450 1452 'x', S_ISUID, 'S', '-' };
1451 1453 static int m3[] = { 1, S_IRGRP, 'r', '-' };
1452 1454 static int m4[] = { 1, S_IWGRP, 'w', '-' };
1453 1455 static int m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
1454 1456 'x', S_ISGID|LS_NOTREG, 'S',
1455 1457 #ifdef XPG4
1456 1458 S_ISGID, 'L', '-'};
1457 1459 #else
1458 1460 S_ISGID, 'l', '-'};
1459 1461 #endif
1460 1462 static int m6[] = { 1, S_IROTH, 'r', '-' };
1461 1463 static int m7[] = { 1, S_IWOTH, 'w', '-' };
1462 1464 static int m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
1463 1465 'x', S_ISVTX, 'T', '-'};
1464 1466
1465 1467 static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
1466 1468
1467 1469 int **mp;
1468 1470
1469 1471 flags = aflag;
1470 1472 for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
1471 1473 selection(*mp);
1472 1474 }
1473 1475
1474 1476 static void
1475 1477 selection(int *pairp)
1476 1478 {
1477 1479 int n;
1478 1480
1479 1481 n = *pairp++;
1480 1482 while (n-->0) {
1481 1483 if ((flags & *pairp) == *pairp) {
1482 1484 pairp++;
1483 1485 break;
1484 1486 } else {
1485 1487 pairp += 2;
1486 1488 }
1487 1489 }
1488 1490 (void) putchar(*pairp);
1489 1491 curcol++;
1490 1492 }
1491 1493
1492 1494 /*
1493 1495 * column: get to the beginning of the next column.
1494 1496 */
1495 1497 static void
1496 1498 column(void)
1497 1499 {
1498 1500 if (curcol == 0)
1499 1501 return;
1500 1502 if (mflg) {
1501 1503 (void) putc(',', stdout);
1502 1504 curcol++;
1503 1505 if (curcol + colwidth + 2 > num_cols) {
1504 1506 (void) putc('\n', stdout);
1505 1507 curcol = 0;
1506 1508 return;
1507 1509 }
1508 1510 (void) putc(' ', stdout);
1509 1511 curcol++;
1510 1512 return;
1511 1513 }
1512 1514 if (Cflg == 0) {
1513 1515 (void) putc('\n', stdout);
1514 1516 curcol = 0;
1515 1517 return;
1516 1518 }
1517 1519 if ((curcol / colwidth + 2) * colwidth > num_cols) {
1518 1520 (void) putc('\n', stdout);
1519 1521 curcol = 0;
1520 1522 return;
1521 1523 }
1522 1524 do {
1523 1525 (void) putc(' ', stdout);
1524 1526 curcol++;
1525 1527 } while (curcol % colwidth);
1526 1528 }
1527 1529
1528 1530 static void
1529 1531 new_line(void)
1530 1532 {
1531 1533 if (curcol) {
1532 1534 first = 0;
1533 1535 (void) putc('\n', stdout);
1534 1536 curcol = 0;
1535 1537 }
1536 1538 }
1537 1539
1538 1540 /*
1539 1541 * read each filename in directory dir and store its
1540 1542 * status in flist[nfiles]
1541 1543 * use makename() to form pathname dir/filename;
1542 1544 */
1543 1545 static void
1544 1546 rddir(char *dir, struct ditem *myinfo)
1545 1547 {
1546 1548 struct dirent *dentry;
1547 1549 DIR *dirf;
1548 1550 int j;
1549 1551 struct lbuf *ep;
1550 1552 int width;
1551 1553
1552 1554 if ((dirf = opendir(dir)) == NULL) {
1553 1555 (void) fflush(stdout);
1554 1556 perror(dir);
1555 1557 err = 2;
1556 1558 return;
1557 1559 } else {
1558 1560 tblocks = 0;
1559 1561 for (;;) {
1560 1562 errno = 0;
1561 1563 if ((dentry = readdir(dirf)) == NULL)
1562 1564 break;
1563 1565 if (aflg == 0 && dentry->d_name[0] == '.' &&
1564 1566 (Aflg == 0 ||
1565 1567 dentry->d_name[1] == '\0' ||
1566 1568 (dentry->d_name[1] == '.' &&
1567 1569 dentry->d_name[2] == '\0')))
1568 1570 /*
1569 1571 * check for directory items '.', '..',
1570 1572 * and items without valid inode-number;
1571 1573 */
1572 1574 continue;
1573 1575
1574 1576 /* skip entries ending in ~ if -B was given */
1575 1577 if (Bflg &&
1576 1578 dentry->d_name[strlen(dentry->d_name) - 1] == '~')
1577 1579 continue;
1578 1580 if (Cflg || mflg) {
1579 1581 width = strcol((unsigned char *)dentry->d_name);
1580 1582 if (width > filewidth)
1581 1583 filewidth = width;
1582 1584 }
1583 1585 ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
1584 1586 if (ep == NULL) {
1585 1587 if (nomocore)
1586 1588 exit(2);
1587 1589 continue;
1588 1590 } else {
1589 1591 ep->lnum = dentry->d_ino;
1590 1592 for (j = 0; dentry->d_name[j] != '\0'; j++)
1591 1593 ep->ln.lname[j] = dentry->d_name[j];
1592 1594 ep->ln.lname[j] = '\0';
1593 1595
1594 1596 /*
1595 1597 * Since this entry doesn't need to be sorted
1596 1598 * or further processed, print it right away.
1597 1599 */
1598 1600 if (noflist) {
1599 1601 pem(&ep, &ep + 1, 0);
1600 1602 nfiles--;
1601 1603 }
1602 1604 }
1603 1605 }
1604 1606 if (errno) {
1605 1607 int sav_errno = errno;
1606 1608
1607 1609 (void) fprintf(stderr,
1608 1610 gettext("ls: error reading directory %s: %s\n"),
1609 1611 dir, strerror(sav_errno));
1610 1612 }
1611 1613 (void) closedir(dirf);
1612 1614 colwidth = fixedwidth + filewidth;
1613 1615 }
1614 1616 }
1615 1617
1616 1618 /*
1617 1619 * Attaching a link to an inode's ancestors. Search
1618 1620 * through the ancestors to check for cycles (an inode which
1619 1621 * we have already tracked in this inodes ancestry). If a cycle
1620 1622 * is detected, set the exit code and record the fact so that
1621 1623 * it is reported at the right time when printing the directory.
1622 1624 * In addition, set the exit code. Note: If the -a flag was
1623 1625 * specified, we don't want to check for cycles for directories
1624 1626 * ending in '/.' or '/..' unless they were specified on the
1625 1627 * command line.
1626 1628 */
1627 1629 static void
1628 1630 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
1629 1631 int argfl, struct ditem *myparent)
1630 1632 {
1631 1633 size_t file_len;
1632 1634 struct ditem *myinfo;
1633 1635 struct ditem *tptr;
1634 1636
1635 1637 file_len = strlen(file);
1636 1638 if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
1637 1639 NOTPARENTDIR(file, file_len))) {
1638 1640 /*
1639 1641 * Add this inode's ancestry
1640 1642 * info and insert it into the
1641 1643 * ancestry list by pointing
1642 1644 * back to its parent. We save
1643 1645 * it (in rep) with the other info
1644 1646 * we're gathering for this inode.
1645 1647 */
1646 1648 if ((myinfo = malloc(
1647 1649 sizeof (struct ditem))) == NULL) {
1648 1650 perror("ls");
1649 1651 exit(2);
1650 1652 }
1651 1653 myinfo->dev = pstatb->st_dev;
1652 1654 myinfo->ino = pstatb->st_ino;
1653 1655 myinfo->parent = myparent;
1654 1656 rep->ancinfo = myinfo;
1655 1657
1656 1658 /*
1657 1659 * If this node has the same device id and
1658 1660 * inode number of one of its ancestors,
1659 1661 * then we've detected a cycle.
1660 1662 */
1661 1663 if (myparent != NULL) {
1662 1664 for (tptr = myparent; tptr->parent != NULL;
1663 1665 tptr = tptr->parent) {
1664 1666 if ((tptr->dev == pstatb->st_dev) &&
1665 1667 (tptr->ino == pstatb->st_ino)) {
1666 1668 /*
1667 1669 * Cycle detected for this
1668 1670 * directory. Record the fact
1669 1671 * it is a cycle so we don't
1670 1672 * try to process this
1671 1673 * directory as we are
1672 1674 * walking through the
1673 1675 * list of directories.
1674 1676 */
1675 1677 rep->cycle = 1;
1676 1678 err = 2;
1677 1679 break;
1678 1680 }
1679 1681 }
1680 1682 }
1681 1683 }
1682 1684 }
1683 1685
1684 1686 /*
1685 1687 * Do re-calculate the mode for group for ACE_T type of acls.
1686 1688 * This is because, if the server's FS happens to be UFS, supporting
1687 1689 * POSIX ACL's, then it does a special calculation of group mode
1688 1690 * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
1689 1691 *
1690 1692 * This algorithm is from the NFSv4 ACL Draft. Here a part of that
1691 1693 * algorithm is used for the group mode calculation only.
1692 1694 * What is modified here from the algorithm is that only the
1693 1695 * entries with flags ACE_GROUP are considered. For each entry
1694 1696 * with ACE_GROUP flag, the first occurance of a specific access
1695 1697 * is checked if it is allowed.
1696 1698 * We are not interested in perms for user and other, as they
1697 1699 * were taken from st_mode value.
1698 1700 * We are not interested in a_who field of ACE, as we need just
1699 1701 * unix mode bits for the group.
1700 1702 */
1701 1703
1702 1704 #define OWNED_GROUP (ACE_GROUP | ACE_IDENTIFIER_GROUP)
1703 1705 #define IS_TYPE_ALLOWED(type) ((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1704 1706
1705 1707 int
1706 1708 grp_mask_to_mode(struct lbuf *p)
1707 1709 {
1708 1710 int mode = 0, seen = 0;
1709 1711 int acecnt;
1710 1712 int flags;
1711 1713 ace_t *ap;
1712 1714 acl_t *acep = p->aclp;
1713 1715
1714 1716 acecnt = acl_cnt(acep);
1715 1717 for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) {
1716 1718
1717 1719 if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE &&
1718 1720 ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE)
1719 1721 continue;
1720 1722
1721 1723 if (ap->a_flags & ACE_INHERIT_ONLY_ACE)
1722 1724 continue;
1723 1725
1724 1726 /*
1725 1727 * if it is first group@ or first everyone@
1726 1728 * for each of read, write and execute, then
1727 1729 * that will be the group mode bit.
1728 1730 */
1729 1731 flags = ap->a_flags & ACE_TYPE_FLAGS;
1730 1732 if (flags == OWNED_GROUP || (flags == ACE_IDENTIFIER_GROUP &&
1731 1733 ap->a_who == p->lgid) || flags == ACE_EVERYONE) {
1732 1734 if (ap->a_access_mask & ACE_READ_DATA) {
1733 1735 if (!(seen & S_IRGRP)) {
1734 1736 seen |= S_IRGRP;
1735 1737 if (IS_TYPE_ALLOWED(ap->a_type))
1736 1738 mode |= S_IRGRP;
1737 1739 }
1738 1740 }
1739 1741 if (ap->a_access_mask & ACE_WRITE_DATA) {
1740 1742 if (!(seen & S_IWGRP)) {
1741 1743 seen |= S_IWGRP;
1742 1744 if (IS_TYPE_ALLOWED(ap->a_type))
1743 1745 mode |= S_IWGRP;
1744 1746 }
1745 1747 }
1746 1748 if (ap->a_access_mask & ACE_EXECUTE) {
1747 1749 if (!(seen & S_IXGRP)) {
1748 1750 seen |= S_IXGRP;
1749 1751 if (IS_TYPE_ALLOWED(ap->a_type))
1750 1752 mode |= S_IXGRP;
1751 1753 }
1752 1754 }
1753 1755 }
1754 1756 }
1755 1757 return (mode);
1756 1758 }
1757 1759
1758 1760 /*
1759 1761 * get status of file and recomputes tblocks;
1760 1762 * argfl = 1 if file is a name in ls-command and = 0
1761 1763 * for filename in a directory whose name is an
1762 1764 * argument in the command;
1763 1765 * stores a pointer in flist[nfiles] and
1764 1766 * returns that pointer;
1765 1767 * returns NULL if failed;
1766 1768 */
1767 1769 static struct lbuf *
1768 1770 gstat(char *file, int argfl, struct ditem *myparent)
1769 1771 {
1770 1772 struct stat statb, statb1;
1771 1773 struct lbuf *rep;
1772 1774 char buf[BUFSIZ];
1773 1775 ssize_t cc;
1774 1776 int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
1775 1777 int aclcnt;
1776 1778 int error;
1777 1779 aclent_t *tp;
1778 1780 o_mode_t groupperm, mask;
1779 1781 int grouppermfound, maskfound;
1780 1782
1781 1783 if (nomocore)
1782 1784 return (NULL);
1783 1785
1784 1786 if (nfiles >= maxfils) {
1785 1787 /*
1786 1788 * all flist/lbuf pair assigned files, time to get some
1787 1789 * more space
1788 1790 */
1789 1791 maxfils += quantn;
1790 1792 if (((flist = realloc(flist,
1791 1793 maxfils * sizeof (struct lbuf *))) == NULL) ||
1792 1794 ((nxtlbf = malloc(quantn *
1793 1795 sizeof (struct lbuf))) == NULL)) {
1794 1796 perror("ls");
1795 1797 nomocore = 1;
1796 1798 return (NULL);
1797 1799 }
1798 1800 }
1799 1801
1800 1802 /*
1801 1803 * nfiles is reset to nargs for each directory
1802 1804 * that is given as an argument maxn is checked
1803 1805 * to prevent the assignment of an lbuf to a flist entry
↓ open down ↓ |
530 lines elided |
↑ open up ↑ |
1804 1806 * that already has one assigned.
1805 1807 */
1806 1808 if (nfiles >= maxn) {
1807 1809 rep = nxtlbf++;
1808 1810 flist[nfiles++] = rep;
1809 1811 maxn = nfiles;
1810 1812 } else {
1811 1813 rep = flist[nfiles++];
1812 1814 }
1813 1815
1816 + /* Clear the lbuf */
1817 + (void) memset((void *) rep, 0, sizeof (struct lbuf));
1818 +
1814 1819 /*
1815 1820 * When noflist is set, none of the extra information about the dirent
1816 - * will be printed, so omit initialization of this lbuf as well as the
1817 - * stat(2) call.
1821 + * will be printed, so omit remaining initialization of this lbuf
1822 + * as well as the stat(2) call.
1818 1823 */
1819 1824 if (!argfl && noflist)
1820 1825 return (rep);
1821 1826
1822 - /* Initialize */
1827 + /* Initialize non-zero members */
1823 1828
1824 - rep->lflags = (mode_t)0;
1825 - rep->flinkto = NULL;
1826 - rep->cycle = 0;
1827 1829 rep->lat.tv_sec = time(NULL);
1828 - rep->lat.tv_nsec = 0;
1829 1830 rep->lct.tv_sec = time(NULL);
1830 - rep->lct.tv_nsec = 0;
1831 1831 rep->lmt.tv_sec = time(NULL);
1832 - rep->lmt.tv_nsec = 0;
1833 - rep->aclp = NULL;
1834 - rep->exttr = NULL;
1835 - rep->extm = NULL;
1836 - rep->color = NULL;
1837 - rep->link_color = NULL;
1838 1832
1839 1833 if (argfl || statreq) {
1840 1834 int doacl;
1841 1835
1842 1836 if (lflg)
1843 1837 doacl = 1;
1844 1838 else
1845 1839 doacl = 0;
1846 1840
1847 1841 if ((*statf)(file, &statb) < 0) {
1848 1842 if (argfl || errno != ENOENT ||
1849 1843 (Lflg && lstat(file, &statb) == 0)) {
1850 1844 /*
1851 1845 * Avoid race between readdir and lstat.
1852 1846 * Print error message in case of dangling link.
1853 1847 */
1854 1848 perror(file);
1855 1849 err = 2;
1856 1850 }
1857 1851 nfiles--;
1858 1852 return (NULL);
1859 1853 }
1860 1854
1861 1855 /*
1862 1856 * If -H was specified, and the file linked to was
1863 1857 * not a directory, then we need to get the info
1864 1858 * for the symlink itself.
1865 1859 */
1866 1860 if ((Hflg) && (argfl) &&
1867 1861 ((statb.st_mode & S_IFMT) != S_IFDIR)) {
1868 1862 if (lstat(file, &statb) < 0) {
1869 1863 perror(file);
1870 1864 err = 2;
1871 1865 }
1872 1866 }
1873 1867
1874 1868 rep->lnum = statb.st_ino;
1875 1869 rep->lsize = statb.st_size;
1876 1870 rep->lblocks = statb.st_blocks;
1877 1871 if (colorflg)
1878 1872 rep->color = ls_color_find(file, statb.st_mode);
1879 1873
1880 1874 switch (statb.st_mode & S_IFMT) {
1881 1875 case S_IFDIR:
1882 1876 rep->ltype = 'd';
1883 1877 if (Rflg) {
1884 1878 record_ancestry(file, &statb, rep,
1885 1879 argfl, myparent);
1886 1880 }
1887 1881 break;
1888 1882 case S_IFBLK:
1889 1883 rep->ltype = 'b';
1890 1884 rep->lsize = (off_t)statb.st_rdev;
1891 1885 break;
1892 1886 case S_IFCHR:
1893 1887 rep->ltype = 'c';
1894 1888 rep->lsize = (off_t)statb.st_rdev;
1895 1889 break;
1896 1890 case S_IFIFO:
1897 1891 rep->ltype = 'p';
1898 1892 break;
1899 1893 case S_IFSOCK:
1900 1894 rep->ltype = 's';
1901 1895 rep->lsize = 0;
1902 1896 break;
1903 1897 case S_IFLNK:
1904 1898 /* symbolic links may not have ACLs, so elide acl() */
1905 1899 if ((Lflg == 0) || (Hflg == 0) ||
1906 1900 ((Hflg) && (!argfl))) {
1907 1901 doacl = 0;
1908 1902 }
1909 1903 rep->ltype = 'l';
1910 1904 if (lflg || colorflg) {
1911 1905 cc = readlink(file, buf, BUFSIZ);
1912 1906 if (cc < 0)
1913 1907 break;
1914 1908
1915 1909 /*
1916 1910 * follow the symbolic link
1917 1911 * to generate the appropriate
1918 1912 * Fflg marker for the object
1919 1913 * eg, /bin -> /sym/bin/
1920 1914 */
1921 1915 error = 0;
1922 1916 if (Fflg || pflg || colorflg)
1923 1917 error = stat(file, &statb1);
1924 1918
1925 1919 if (colorflg) {
1926 1920 if (error >= 0)
1927 1921 rep->link_color =
1928 1922 ls_color_find(file,
1929 1923 statb1.st_mode);
1930 1924 else
1931 1925 rep->link_color =
1932 1926 lsc_orphan;
1933 1927 }
1934 1928
1935 1929 if ((Fflg || pflg) && error >= 0) {
1936 1930 switch (statb1.st_mode & S_IFMT) {
1937 1931 case S_IFDIR:
1938 1932 buf[cc++] = '/';
1939 1933 break;
1940 1934 case S_IFSOCK:
1941 1935 buf[cc++] = '=';
1942 1936 break;
1943 1937 case S_IFDOOR:
1944 1938 buf[cc++] = '>';
1945 1939 break;
1946 1940 case S_IFIFO:
1947 1941 buf[cc++] = '|';
1948 1942 break;
1949 1943 default:
1950 1944 if ((statb1.st_mode & ~S_IFMT) &
1951 1945 (S_IXUSR|S_IXGRP| S_IXOTH))
1952 1946 buf[cc++] = '*';
1953 1947 break;
1954 1948 }
1955 1949 }
1956 1950 buf[cc] = '\0';
1957 1951 rep->flinkto = strdup(buf);
1958 1952 if (rep->flinkto == NULL) {
1959 1953 perror("ls");
1960 1954 nomocore = 1;
1961 1955 return (NULL);
1962 1956 }
1963 1957 break;
1964 1958 }
1965 1959
1966 1960 /*
1967 1961 * ls /sym behaves differently from ls /sym/
1968 1962 * when /sym is a symbolic link. This is fixed
1969 1963 * when explicit arguments are specified.
1970 1964 */
1971 1965
1972 1966 #ifdef XPG6
1973 1967 /* Do not follow a symlink when -F is specified */
1974 1968 if ((!argfl) || (argfl && Fflg) ||
1975 1969 (stat(file, &statb1) < 0))
1976 1970 #else
1977 1971 /* Follow a symlink when -F is specified */
1978 1972 if (!argfl || stat(file, &statb1) < 0)
1979 1973 #endif /* XPG6 */
1980 1974 break;
1981 1975 if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
1982 1976 statb = statb1;
1983 1977 rep->ltype = 'd';
1984 1978 rep->lsize = statb1.st_size;
1985 1979 if (Rflg) {
1986 1980 record_ancestry(file, &statb, rep,
1987 1981 argfl, myparent);
1988 1982 }
1989 1983 }
1990 1984 break;
1991 1985 case S_IFDOOR:
1992 1986 rep->ltype = 'D';
1993 1987 break;
1994 1988 case S_IFREG:
1995 1989 rep->ltype = '-';
1996 1990 break;
1997 1991 case S_IFPORT:
1998 1992 rep->ltype = 'P';
1999 1993 break;
2000 1994 default:
2001 1995 rep->ltype = '?';
2002 1996 break;
2003 1997 }
2004 1998 rep->lflags = statb.st_mode & ~S_IFMT;
2005 1999
2006 2000 if (!S_ISREG(statb.st_mode))
2007 2001 rep->lflags |= LS_NOTREG;
2008 2002
2009 2003 rep->luid = statb.st_uid;
2010 2004 rep->lgid = statb.st_gid;
2011 2005 rep->lnl = statb.st_nlink;
2012 2006 if (uflg || (tmflg && atm))
2013 2007 rep->lmtime = statb.st_atim;
2014 2008 else if (cflg || (tmflg && ctm))
2015 2009 rep->lmtime = statb.st_ctim;
2016 2010 else
2017 2011 rep->lmtime = statb.st_mtim;
2018 2012 rep->lat = statb.st_atim;
2019 2013 rep->lct = statb.st_ctim;
2020 2014 rep->lmt = statb.st_mtim;
2021 2015
2022 2016 /* ACL: check acl entries count */
2023 2017 if (doacl) {
2024 2018
2025 2019 error = acl_get(file, 0, &rep->aclp);
2026 2020 if (error) {
2027 2021 (void) fprintf(stderr,
2028 2022 gettext("ls: can't read ACL on %s: %s\n"),
2029 2023 file, acl_strerror(error));
2030 2024 rep->acl = ' ';
2031 2025 acl_err++;
2032 2026 return (rep);
2033 2027 }
2034 2028
2035 2029 rep->acl = ' ';
2036 2030
2037 2031 if (rep->aclp &&
2038 2032 ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
2039 2033 rep->acl = '+';
2040 2034 /*
2041 2035 * Special handling for ufs aka aclent_t ACL's
2042 2036 */
2043 2037 if (acl_type(rep->aclp) == ACLENT_T) {
2044 2038 /*
2045 2039 * For files with non-trivial acls, the
2046 2040 * effective group permissions are the
2047 2041 * intersection of the GROUP_OBJ value
2048 2042 * and the CLASS_OBJ (acl mask) value.
2049 2043 * Determine both the GROUP_OBJ and
2050 2044 * CLASS_OBJ for this file and insert
2051 2045 * the logical AND of those two values
2052 2046 * in the group permissions field
2053 2047 * of the lflags value for this file.
2054 2048 */
2055 2049
2056 2050 /*
2057 2051 * Until found in acl list, assume
2058 2052 * maximum permissions for both group
2059 2053 * a nd mask. (Just in case the acl
2060 2054 * lacks either value for some reason.)
2061 2055 */
2062 2056 groupperm = 07;
2063 2057 mask = 07;
2064 2058 grouppermfound = 0;
2065 2059 maskfound = 0;
2066 2060 aclcnt = acl_cnt(rep->aclp);
2067 2061 for (tp =
2068 2062 (aclent_t *)acl_data(rep->aclp);
2069 2063 aclcnt--; tp++) {
2070 2064 if (tp->a_type == GROUP_OBJ) {
2071 2065 groupperm = tp->a_perm;
2072 2066 grouppermfound = 1;
2073 2067 continue;
2074 2068 }
2075 2069 if (tp->a_type == CLASS_OBJ) {
2076 2070 mask = tp->a_perm;
2077 2071 maskfound = 1;
2078 2072 }
2079 2073 if (grouppermfound && maskfound)
2080 2074 break;
2081 2075 }
2082 2076
2083 2077
2084 2078 /* reset all the group bits */
2085 2079 rep->lflags &= ~S_IRWXG;
2086 2080
2087 2081 /*
2088 2082 * Now set them to the logical AND of
2089 2083 * the GROUP_OBJ permissions and the
2090 2084 * acl mask.
2091 2085 */
2092 2086
2093 2087 rep->lflags |= (groupperm & mask) << 3;
2094 2088
2095 2089 } else if (acl_type(rep->aclp) == ACE_T) {
2096 2090 int mode;
2097 2091 mode = grp_mask_to_mode(rep);
2098 2092 rep->lflags &= ~S_IRWXG;
2099 2093 rep->lflags |= mode;
2100 2094 }
2101 2095 }
2102 2096
2103 2097 if (!vflg && !Vflg && rep->aclp) {
2104 2098 acl_free(rep->aclp);
2105 2099 rep->aclp = NULL;
2106 2100 }
2107 2101
2108 2102 if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
2109 2103 rep->acl = '@';
2110 2104
2111 2105 } else
2112 2106 rep->acl = ' ';
2113 2107
2114 2108 /* mask ISARG and other file-type bits */
2115 2109
2116 2110 if (rep->ltype != 'b' && rep->ltype != 'c')
2117 2111 tblocks += rep->lblocks;
2118 2112
2119 2113 /* Get extended system attributes */
2120 2114
2121 2115 if ((saflg || (tmflg && crtm) || (tmflg && alltm)) &&
2122 2116 (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) {
2123 2117 int i;
2124 2118
2125 2119 sacnt = attr_count();
2126 2120 /*
2127 2121 * Allocate 'sacnt' size array to hold extended
2128 2122 * system attribute name (verbose) or respective
2129 2123 * symbol represenation (compact).
2130 2124 */
2131 2125 rep->exttr = xmalloc(sacnt * sizeof (struct attrb),
2132 2126 rep);
2133 2127
2134 2128 /* initialize boolean attribute list */
2135 2129 for (i = 0; i < sacnt; i++)
2136 2130 rep->exttr[i].name = NULL;
2137 2131 if (get_sysxattr(file, rep) != 0) {
2138 2132 (void) fprintf(stderr,
2139 2133 gettext("ls:Failed to retrieve "
2140 2134 "extended system attribute from "
2141 2135 "%s\n"), file);
2142 2136 rep->exttr[0].name = xmalloc(2, rep);
2143 2137 (void) strlcpy(rep->exttr[0].name, "?", 2);
2144 2138 }
2145 2139 }
2146 2140 }
2147 2141 return (rep);
2148 2142 }
2149 2143
2150 2144 /*
2151 2145 * returns pathname of the form dir/file;
2152 2146 * dir and file are null-terminated strings.
2153 2147 */
2154 2148 static char *
2155 2149 makename(char *dir, char *file)
2156 2150 {
2157 2151 /*
2158 2152 * PATH_MAX is the maximum length of a path name.
2159 2153 * MAXNAMLEN is the maximum length of any path name component.
2160 2154 * Allocate space for both, plus the '/' in the middle
2161 2155 * and the null character at the end.
2162 2156 * dfile is static as this is returned by makename().
2163 2157 */
2164 2158 static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
2165 2159 char *dp, *fp;
2166 2160
2167 2161 dp = dfile;
2168 2162 fp = dir;
2169 2163 while (*fp)
2170 2164 *dp++ = *fp++;
2171 2165 if (dp > dfile && *(dp - 1) != '/')
2172 2166 *dp++ = '/';
2173 2167 fp = file;
2174 2168 while (*fp)
2175 2169 *dp++ = *fp++;
2176 2170 *dp = '\0';
2177 2171 return (dfile);
2178 2172 }
2179 2173
2180 2174
2181 2175 #include <pwd.h>
2182 2176 #include <grp.h>
2183 2177 #include <utmpx.h>
2184 2178
2185 2179 struct utmpx utmp;
2186 2180
2187 2181 #define NMAX (sizeof (utmp.ut_name))
2188 2182 #define SCPYN(a, b) (void) strncpy(a, b, NMAX)
2189 2183
2190 2184
2191 2185 struct cachenode { /* this struct must be zeroed before using */
2192 2186 struct cachenode *lesschild; /* subtree whose entries < val */
2193 2187 struct cachenode *grtrchild; /* subtree whose entries > val */
2194 2188 long val; /* the uid or gid of this entry */
2195 2189 int initted; /* name has been filled in */
2196 2190 char name[NMAX+1]; /* the string that val maps to */
2197 2191 };
2198 2192 static struct cachenode *names, *groups;
2199 2193
2200 2194 static struct cachenode *
2201 2195 findincache(struct cachenode **head, long val)
2202 2196 {
2203 2197 struct cachenode **parent = head;
2204 2198 struct cachenode *c = *parent;
2205 2199
2206 2200 while (c != NULL) {
2207 2201 if (val == c->val) {
2208 2202 /* found it */
2209 2203 return (c);
2210 2204 } else if (val < c->val) {
2211 2205 parent = &c->lesschild;
2212 2206 c = c->lesschild;
2213 2207 } else {
2214 2208 parent = &c->grtrchild;
2215 2209 c = c->grtrchild;
2216 2210 }
2217 2211 }
2218 2212
2219 2213 /* not in the cache, make a new entry for it */
2220 2214 c = calloc(1, sizeof (struct cachenode));
2221 2215 if (c == NULL) {
2222 2216 perror("ls");
2223 2217 exit(2);
2224 2218 }
2225 2219 *parent = c;
2226 2220 c->val = val;
2227 2221 return (c);
2228 2222 }
2229 2223
2230 2224 /*
2231 2225 * get name from cache, or passwd file for a given uid;
2232 2226 * lastuid is set to uid.
2233 2227 */
2234 2228 static char *
2235 2229 getname(uid_t uid)
2236 2230 {
2237 2231 struct passwd *pwent;
2238 2232 struct cachenode *c;
2239 2233
2240 2234 if ((uid == lastuid) && lastuname)
2241 2235 return (lastuname);
2242 2236
2243 2237 c = findincache(&names, uid);
2244 2238 if (c->initted == 0) {
2245 2239 if ((pwent = getpwuid(uid)) != NULL) {
2246 2240 SCPYN(&c->name[0], pwent->pw_name);
2247 2241 } else {
2248 2242 (void) sprintf(&c->name[0], "%-8u", (int)uid);
2249 2243 }
2250 2244 c->initted = 1;
2251 2245 }
2252 2246 lastuid = uid;
2253 2247 lastuname = &c->name[0];
2254 2248 return (lastuname);
2255 2249 }
2256 2250
2257 2251 /*
2258 2252 * get name from cache, or group file for a given gid;
2259 2253 * lastgid is set to gid.
2260 2254 */
2261 2255 static char *
2262 2256 getgroup(gid_t gid)
2263 2257 {
2264 2258 struct group *grent;
2265 2259 struct cachenode *c;
2266 2260
2267 2261 if ((gid == lastgid) && lastgname)
2268 2262 return (lastgname);
2269 2263
2270 2264 c = findincache(&groups, gid);
2271 2265 if (c->initted == 0) {
2272 2266 if ((grent = getgrgid(gid)) != NULL) {
2273 2267 SCPYN(&c->name[0], grent->gr_name);
2274 2268 } else {
2275 2269 (void) sprintf(&c->name[0], "%-8u", (int)gid);
2276 2270 }
2277 2271 c->initted = 1;
2278 2272 }
2279 2273 lastgid = gid;
2280 2274 lastgname = &c->name[0];
2281 2275 return (lastgname);
2282 2276 }
2283 2277
2284 2278 /* return >0 if item pointed by pp2 should appear first */
2285 2279 static int
2286 2280 compar(struct lbuf **pp1, struct lbuf **pp2)
2287 2281 {
2288 2282 struct lbuf *p1, *p2;
2289 2283
2290 2284 p1 = *pp1;
2291 2285 p2 = *pp2;
2292 2286 if (dflg == 0) {
2293 2287 /*
2294 2288 * compare two names in ls-command one of which is file
2295 2289 * and the other is a directory;
2296 2290 * this portion is not used for comparing files within
2297 2291 * a directory name of ls-command;
2298 2292 */
2299 2293 if (p1->lflags&ISARG && p1->ltype == 'd') {
2300 2294 if (!(p2->lflags&ISARG && p2->ltype == 'd'))
2301 2295 return (1);
2302 2296 } else {
2303 2297 if (p2->lflags&ISARG && p2->ltype == 'd')
2304 2298 return (-1);
2305 2299 }
2306 2300 }
2307 2301 if (tflg) {
2308 2302 if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
2309 2303 return (rflg);
2310 2304 else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
2311 2305 return (-rflg);
2312 2306 /* times are equal to the sec, check nsec */
2313 2307 if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
2314 2308 return (rflg);
2315 2309 else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
2316 2310 return (-rflg);
2317 2311 /* if times are equal, fall through and sort by name */
2318 2312 } else if (Sflg) {
2319 2313 /*
2320 2314 * The size stored in lsize can be either the
2321 2315 * size or the major minor number (in the case of
2322 2316 * block and character special devices). If it's
2323 2317 * a major minor number, then the size is considered
2324 2318 * to be zero and we want to fall through and sort
2325 2319 * by name. In addition, if the size of p2 is equal
2326 2320 * to the size of p1 we want to fall through and
2327 2321 * sort by name.
2328 2322 */
2329 2323 off_t p1size = (p1->ltype == 'b') ||
2330 2324 (p1->ltype == 'c') ? 0 : p1->lsize;
2331 2325 off_t p2size = (p2->ltype == 'b') ||
2332 2326 (p2->ltype == 'c') ? 0 : p2->lsize;
2333 2327 if (p2size > p1size) {
2334 2328 return (rflg);
2335 2329 } else if (p2size < p1size) {
2336 2330 return (-rflg);
2337 2331 }
2338 2332 /* Sizes are equal, fall through and sort by name. */
2339 2333 }
2340 2334 return (rflg * strcoll(
2341 2335 p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
2342 2336 p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
2343 2337 }
2344 2338
2345 2339 static void
2346 2340 pprintf(char *s1, char *s2)
2347 2341 {
2348 2342 csi_pprintf((unsigned char *)s1);
2349 2343 csi_pprintf((unsigned char *)s2);
2350 2344 }
2351 2345
2352 2346 static void
2353 2347 csi_pprintf(unsigned char *s)
2354 2348 {
2355 2349 unsigned char *cp;
2356 2350 char c;
2357 2351 int i;
2358 2352 int c_len;
2359 2353 int p_col;
2360 2354 wchar_t pcode;
2361 2355
2362 2356 if (!qflg && !bflg) {
2363 2357 for (cp = s; *cp != '\0'; cp++) {
2364 2358 (void) putchar(*cp);
2365 2359 curcol++;
2366 2360 }
2367 2361 return;
2368 2362 }
2369 2363
2370 2364 for (cp = s; *cp; ) {
2371 2365 if (isascii(c = *cp)) {
2372 2366 if (!isprint(c)) {
2373 2367 if (qflg) {
2374 2368 c = '?';
2375 2369 } else {
2376 2370 curcol += 3;
2377 2371 (void) putc('\\', stdout);
2378 2372 c = '0' + ((*cp >> 6) & 07);
2379 2373 (void) putc(c, stdout);
2380 2374 c = '0' + ((*cp >> 3) & 07);
2381 2375 (void) putc(c, stdout);
2382 2376 c = '0' + (*cp & 07);
2383 2377 }
2384 2378 }
2385 2379 curcol++;
2386 2380 cp++;
2387 2381 (void) putc(c, stdout);
2388 2382 continue;
2389 2383 }
2390 2384
2391 2385 if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
2392 2386 c_len = 1;
2393 2387 goto not_print;
2394 2388 }
2395 2389
2396 2390 if ((p_col = wcwidth(pcode)) > 0) {
2397 2391 (void) putwchar(pcode);
2398 2392 cp += c_len;
2399 2393 curcol += p_col;
2400 2394 continue;
2401 2395 }
2402 2396
2403 2397 not_print:
2404 2398 for (i = 0; i < c_len; i++) {
2405 2399 if (qflg) {
2406 2400 c = '?';
2407 2401 } else {
2408 2402 curcol += 3;
2409 2403 (void) putc('\\', stdout);
2410 2404 c = '0' + ((*cp >> 6) & 07);
2411 2405 (void) putc(c, stdout);
2412 2406 c = '0' + ((*cp >> 3) & 07);
2413 2407 (void) putc(c, stdout);
2414 2408 c = '0' + (*cp & 07);
2415 2409 }
2416 2410 curcol++;
2417 2411 (void) putc(c, stdout);
2418 2412 cp++;
2419 2413 }
2420 2414 }
2421 2415 }
2422 2416
2423 2417 static int
2424 2418 strcol(unsigned char *s1)
2425 2419 {
2426 2420 int w;
2427 2421 int w_col;
2428 2422 int len;
2429 2423 wchar_t wc;
2430 2424
2431 2425 w = 0;
2432 2426 while (*s1) {
2433 2427 if (isascii(*s1)) {
2434 2428 w++;
2435 2429 s1++;
2436 2430 continue;
2437 2431 }
2438 2432
2439 2433 if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
2440 2434 w++;
2441 2435 s1++;
2442 2436 continue;
2443 2437 }
2444 2438
2445 2439 if ((w_col = wcwidth(wc)) < 0)
2446 2440 w_col = len;
2447 2441 s1 += len;
2448 2442 w += w_col;
2449 2443 }
2450 2444 return (w);
2451 2445 }
2452 2446
2453 2447 /*
2454 2448 * Convert an unsigned long long to a string representation and place the
2455 2449 * result in the caller-supplied buffer.
2456 2450 *
2457 2451 * The number provided is a size in bytes. The number is first
2458 2452 * converted to an integral multiple of 'scale' bytes. This new
2459 2453 * number is then scaled down until it is small enough to be in a good
2460 2454 * human readable format, i.e. in the range 0 thru scale-1. If the
2461 2455 * number used to derive the final number is not a multiple of scale, and
2462 2456 * the final number has only a single significant digit, we compute
2463 2457 * tenths of units to provide a second significant digit.
2464 2458 *
2465 2459 * The value "(unsigned long long)-1" is a special case and is always
2466 2460 * converted to "-1".
2467 2461 *
2468 2462 * A pointer to the caller-supplied buffer is returned.
2469 2463 */
2470 2464 static char *
2471 2465 number_to_scaled_string(
2472 2466 numbuf_t buf, /* put the result here */
2473 2467 unsigned long long number, /* convert this number */
2474 2468 long scale)
2475 2469 {
2476 2470 unsigned long long save;
2477 2471 /* Measurement: kilo, mega, giga, tera, peta, exa */
2478 2472 char *uom = "KMGTPE";
2479 2473
2480 2474 if ((long long)number == (long long)-1) {
2481 2475 (void) strlcpy(buf, "-1", sizeof (numbuf_t));
2482 2476 return (buf);
2483 2477 }
2484 2478
2485 2479 save = number;
2486 2480 number = number / scale;
2487 2481
2488 2482 /*
2489 2483 * Now we have number as a count of scale units.
2490 2484 * If no further scaling is necessary, we round up as appropriate.
2491 2485 *
2492 2486 * The largest value number could have had entering the routine is
2493 2487 * 16 Exabytes, so running off the end of the uom array should
2494 2488 * never happen. We check for that, though, as a guard against
2495 2489 * a breakdown elsewhere in the algorithm.
2496 2490 */
2497 2491 if (number < (unsigned long long)scale) {
2498 2492 if ((save % scale) >= (unsigned long long)(scale / 2)) {
2499 2493 if (++number == (unsigned long long)scale) {
2500 2494 uom++;
2501 2495 number = 1;
2502 2496 }
2503 2497 }
2504 2498 } else {
2505 2499 while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
2506 2500 uom++; /* next unit of measurement */
2507 2501 save = number;
2508 2502 /*
2509 2503 * If we're over half way to the next unit of
2510 2504 * 'scale' bytes (which means we should round
2511 2505 * up), then adding half of 'scale' prior to
2512 2506 * the division will push us into that next
2513 2507 * unit of scale when we perform the division
2514 2508 */
2515 2509 number = (number + (scale / 2)) / scale;
2516 2510 }
2517 2511 }
2518 2512
2519 2513 /* check if we should output a decimal place after the point */
2520 2514 if ((save / scale) < 10) {
2521 2515 /* snprintf() will round for us */
2522 2516 float fnum = (float)save / scale;
2523 2517 (void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
2524 2518 fnum, *uom);
2525 2519 } else {
2526 2520 (void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
2527 2521 number, *uom);
2528 2522 }
2529 2523 return (buf);
2530 2524 }
2531 2525
2532 2526 /* Get extended system attributes and set the display */
2533 2527
2534 2528 int
2535 2529 get_sysxattr(char *fname, struct lbuf *rep)
2536 2530 {
2537 2531 boolean_t value;
2538 2532 data_type_t type;
2539 2533 int error;
2540 2534 char *name;
2541 2535 int i;
2542 2536
2543 2537 if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname,
2544 2538 &response)) != 0) {
2545 2539 perror("ls:getattrat");
2546 2540 return (error);
2547 2541 }
2548 2542
2549 2543 /*
2550 2544 * Allocate 'sacnt' size array to hold extended timestamp
2551 2545 * system attributes and initialize the array.
2552 2546 */
2553 2547 rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep);
2554 2548 for (i = 0; i < sacnt; i++) {
2555 2549 rep->extm[i].stm = 0;
2556 2550 rep->extm[i].nstm = 0;
2557 2551 rep->extm[i].name = NULL;
2558 2552 }
2559 2553 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
2560 2554 name = nvpair_name(pair);
2561 2555 type = nvpair_type(pair);
2562 2556 if (type == DATA_TYPE_BOOLEAN_VALUE) {
2563 2557 error = nvpair_value_boolean_value(pair, &value);
2564 2558 if (error) {
2565 2559 (void) fprintf(stderr,
2566 2560 gettext("nvpair_value_boolean_value "
2567 2561 "failed: error = %d\n"), error);
2568 2562 continue;
2569 2563 }
2570 2564 if (name != NULL)
2571 2565 set_sysattrb_display(name, value, rep);
2572 2566 continue;
2573 2567 } else if (type == DATA_TYPE_UINT64_ARRAY) {
2574 2568 if (name != NULL)
2575 2569 set_sysattrtm_display(name, rep);
2576 2570 continue;
2577 2571 }
2578 2572 }
2579 2573 nvlist_free(response);
2580 2574 return (0);
2581 2575 }
2582 2576
2583 2577 /* Set extended system attribute boolean display */
2584 2578
2585 2579 void
2586 2580 set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep)
2587 2581 {
2588 2582 f_attr_t fattr;
2589 2583 const char *opt;
2590 2584 size_t len;
2591 2585
2592 2586 fattr = name_to_attr(name);
2593 2587 if (fattr != F_ATTR_INVAL && fattr < sacnt) {
2594 2588 if (vopt) {
2595 2589 len = strlen(name);
2596 2590 if (val) {
2597 2591 rep->exttr[fattr].name = xmalloc(len + 1, rep);
2598 2592 (void) strlcpy(rep->exttr[fattr].name, name,
2599 2593 len + 1);
2600 2594 } else {
2601 2595 rep->exttr[fattr].name = xmalloc(len + 3, rep);
2602 2596 (void) snprintf(rep->exttr[fattr].name, len + 3,
2603 2597 "no%s", name);
2604 2598 }
2605 2599 } else {
2606 2600 opt = attr_to_option(fattr);
2607 2601 if (opt != NULL) {
2608 2602 len = strlen(opt);
2609 2603 rep->exttr[fattr].name = xmalloc(len + 1, rep);
2610 2604 if (val)
2611 2605 (void) strlcpy(rep->exttr[fattr].name,
2612 2606 opt, len + 1);
2613 2607 else
2614 2608 (void) strlcpy(rep->exttr[fattr].name,
2615 2609 "-", len + 1);
2616 2610 }
2617 2611 }
2618 2612 }
2619 2613 }
2620 2614
2621 2615 /* Set extended system attribute timestamp display */
2622 2616
2623 2617 void
2624 2618 set_sysattrtm_display(char *name, struct lbuf *rep)
2625 2619 {
2626 2620 uint_t nelem;
2627 2621 uint64_t *value;
2628 2622 int i;
2629 2623 size_t len;
2630 2624
2631 2625 if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) {
2632 2626 if (*value != NULL) {
2633 2627 len = strlen(name);
2634 2628 i = 0;
2635 2629 while (rep->extm[i].stm != 0 && i < sacnt)
2636 2630 i++;
2637 2631 rep->extm[i].stm = value[0];
2638 2632 rep->extm[i].nstm = value[1];
2639 2633 rep->extm[i].name = xmalloc(len + 1, rep);
2640 2634 (void) strlcpy(rep->extm[i].name, name, len + 1);
2641 2635 }
2642 2636 }
2643 2637 }
2644 2638
2645 2639 void
2646 2640 format_time(time_t sec, time_t nsec)
2647 2641 {
2648 2642 const char *fstr = time_fmt_new;
2649 2643 char fmt_buf[FMTSIZE];
2650 2644
2651 2645 if (Eflg) {
2652 2646 (void) snprintf(fmt_buf, FMTSIZE, fstr, nsec);
2653 2647 (void) strftime(time_buf, sizeof (time_buf), fmt_buf,
2654 2648 localtime(&sec));
2655 2649 return;
2656 2650 }
2657 2651
2658 2652 if (sec < year || sec > now)
2659 2653 fstr = time_fmt_old;
2660 2654
2661 2655 /* if a custom time was specified, shouldn't be localized */
2662 2656 (void) strftime(time_buf, sizeof (time_buf),
2663 2657 (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr,
2664 2658 localtime(&sec));
2665 2659 }
2666 2660
2667 2661 void
2668 2662 format_attrtime(struct lbuf *p)
2669 2663 {
2670 2664 int tmattr = 0;
2671 2665 int i;
2672 2666
2673 2667 if (p->extm != NULL) {
2674 2668 for (i = 0; i < sacnt; i++) {
2675 2669 if (p->extm[i].name != NULL) {
2676 2670 tmattr = 1;
2677 2671 break;
2678 2672 }
2679 2673 }
2680 2674 }
2681 2675
2682 2676 if (tmattr) {
2683 2677 const char *old_save = time_fmt_old;
2684 2678 const char *new_save = time_fmt_new;
2685 2679
2686 2680 /* Eflg always sets format to FORMAT_ISO_FULL */
2687 2681 if (!Eflg && !time_custom) {
2688 2682 time_fmt_old = FORMAT_OLD;
2689 2683 time_fmt_new = FORMAT_NEW;
2690 2684 }
2691 2685
2692 2686 format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm);
2693 2687
2694 2688 time_fmt_old = old_save;
2695 2689 time_fmt_new = new_save;
2696 2690 }
2697 2691 }
2698 2692
2699 2693 void
2700 2694 print_time(struct lbuf *p)
2701 2695 {
2702 2696 const char *old_save = time_fmt_old;
2703 2697 const char *new_save = time_fmt_new;
2704 2698
2705 2699 int i = 0;
2706 2700
2707 2701 if (!Eflg) {
2708 2702 time_fmt_old = FORMAT_LONG;
2709 2703 time_fmt_new = FORMAT_LONG;
2710 2704 }
2711 2705
2712 2706 new_line();
2713 2707 format_time(p->lat.tv_sec, p->lat.tv_nsec);
2714 2708 (void) printf(" timestamp: atime %s\n", time_buf);
2715 2709 format_time(p->lct.tv_sec, p->lct.tv_nsec);
2716 2710 (void) printf(" timestamp: ctime %s\n", time_buf);
2717 2711 format_time(p->lmt.tv_sec, p->lmt.tv_nsec);
2718 2712 (void) printf(" timestamp: mtime %s\n", time_buf);
2719 2713 if (p->extm != NULL) {
2720 2714 while (p->extm[i].nstm != 0 && i < sacnt) {
2721 2715 format_time(p->extm[i].stm, p->extm[i].nstm);
2722 2716 if (p->extm[i].name != NULL) {
2723 2717 (void) printf(" timestamp:"
2724 2718 " %s %s\n",
2725 2719 p->extm[i].name, time_buf);
2726 2720 }
2727 2721 i++;
2728 2722 }
2729 2723 }
2730 2724
2731 2725 time_fmt_old = old_save;
2732 2726 time_fmt_new = new_save;
2733 2727 }
2734 2728
2735 2729 /*
2736 2730 * Check if color definition applies to entry, returns 1 if yes, 0 if no
2737 2731 */
2738 2732 static int
2739 2733 color_match(const char *fname, mode_t mode, ls_color_t *color)
2740 2734 {
2741 2735 switch (color->ftype) {
2742 2736 case LS_PAT:
2743 2737 {
2744 2738 size_t fname_len, sfx_len;
2745 2739
2746 2740 fname_len = strlen(fname);
2747 2741 sfx_len = strlen(color->sfx);
2748 2742 if (sfx_len > fname_len)
2749 2743 return (0);
2750 2744
2751 2745 if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0)
2752 2746 return (1);
2753 2747 else
2754 2748 return (0);
2755 2749 }
2756 2750
2757 2751 case LS_NORMAL:
2758 2752 return (1);
2759 2753
2760 2754 case LS_FILE:
2761 2755 return (S_ISREG(mode));
2762 2756
2763 2757 case LS_DIR:
2764 2758 return (S_ISDIR(mode));
2765 2759
2766 2760 case LS_LINK:
2767 2761 return (S_ISLNK(mode));
2768 2762
2769 2763 case LS_FIFO:
2770 2764 return (S_ISFIFO(mode));
2771 2765
2772 2766 case LS_SOCK:
2773 2767 return (S_ISSOCK(mode));
2774 2768
2775 2769 case LS_DOOR:
2776 2770 return (S_ISDOOR(mode));
2777 2771
2778 2772 case LS_BLK:
2779 2773 return (S_ISBLK(mode));
2780 2774
2781 2775 case LS_CHR:
2782 2776 return (S_ISCHR(mode));
2783 2777
2784 2778 case LS_PORT:
2785 2779 return (S_ISPORT(mode));
2786 2780
2787 2781 case LS_ORPHAN:
2788 2782 /* this is tested for by gstat */
2789 2783 return (0);
2790 2784
2791 2785 case LS_SETUID:
2792 2786 return (!S_ISLNK(mode) && (mode & S_ISUID));
2793 2787
2794 2788 case LS_SETGID:
2795 2789 return (!S_ISLNK(mode) && (mode & S_ISGID));
2796 2790
2797 2791 case LS_STICKY_OTHER_WRITABLE:
2798 2792 return (!S_ISLNK(mode) && (mode & (S_IWOTH|S_ISVTX)));
2799 2793
2800 2794 case LS_OTHER_WRITABLE:
2801 2795 return (!S_ISLNK(mode) && (mode & S_IWOTH));
2802 2796
2803 2797 case LS_STICKY:
2804 2798 return (!S_ISLNK(mode) && (mode & S_ISVTX));
2805 2799
2806 2800 case LS_EXEC:
2807 2801 return (!S_ISLNK(mode) && (mode & (S_IXUSR|S_IXGRP|S_IXOTH)));
2808 2802 }
2809 2803
2810 2804 return (0);
2811 2805 }
2812 2806
2813 2807 static void
2814 2808 dump_color(ls_color_t *c)
2815 2809 {
2816 2810 if (c == NULL)
2817 2811 return;
2818 2812
2819 2813 (void) printf("\n\ttype: ");
2820 2814 switch (c->ftype) {
2821 2815 case LS_NORMAL:
2822 2816 (void) printf("LS_NORMAL");
2823 2817 break;
2824 2818 case LS_FILE:
2825 2819 (void) printf("LS_FILE");
2826 2820 break;
2827 2821 case LS_EXEC:
2828 2822 (void) printf("LS_EXEC");
2829 2823 break;
2830 2824 case LS_DIR:
2831 2825 (void) printf("LS_DIR");
2832 2826 break;
2833 2827 case LS_LINK:
2834 2828 (void) printf("LS_LINK");
2835 2829 break;
2836 2830
2837 2831 case LS_FIFO:
2838 2832 (void) printf("LS_FIFO");
2839 2833 break;
2840 2834
2841 2835 case LS_SOCK:
2842 2836 (void) printf("LS_SOCK");
2843 2837 break;
2844 2838
2845 2839 case LS_DOOR:
2846 2840 (void) printf("LS_DOOR");
2847 2841 break;
2848 2842
2849 2843 case LS_BLK:
2850 2844 (void) printf("LS_BLK");
2851 2845 break;
2852 2846
2853 2847 case LS_CHR:
2854 2848 (void) printf("LS_CHR");
2855 2849 break;
2856 2850
2857 2851 case LS_PORT:
2858 2852 (void) printf("LS_PORT");
2859 2853 break;
2860 2854
2861 2855 case LS_STICKY:
2862 2856 (void) printf("LS_STICKY");
2863 2857 break;
2864 2858
2865 2859 case LS_ORPHAN:
2866 2860 (void) printf("LS_ORPHAN");
2867 2861 break;
2868 2862
2869 2863 case LS_SETGID:
2870 2864 (void) printf("LS_SETGID");
2871 2865 break;
2872 2866
2873 2867 case LS_SETUID:
2874 2868 (void) printf("LS_SETUID");
2875 2869 break;
2876 2870
2877 2871 case LS_OTHER_WRITABLE:
2878 2872 (void) printf("LS_OTHER_WRITABLE");
2879 2873 break;
2880 2874
2881 2875 case LS_STICKY_OTHER_WRITABLE:
2882 2876 (void) printf("LS_STICKY_OTHER_WRITABLE");
2883 2877 break;
2884 2878
2885 2879 case LS_PAT:
2886 2880 (void) printf("LS_PAT\n");
2887 2881 (void) printf("\tpattern: %s", c->sfx);
2888 2882 break;
2889 2883 }
2890 2884 (void) printf("\n");
2891 2885 (void) printf("\tattr: %d\n", c->attr);
2892 2886 (void) printf("\tfg: %d\n", c->fg);
2893 2887 (void) printf("\tbg: %d\n", c->bg);
2894 2888 (void) printf("\t");
2895 2889 }
2896 2890
2897 2891 static ls_color_t *
2898 2892 ls_color_find(const char *fname, mode_t mode)
2899 2893 {
2900 2894 int i;
2901 2895
2902 2896 /*
2903 2897 * Colors are sorted from most general lsc_colors[0] to most specific
2904 2898 * lsc_colors[lsc_ncolors - 1] by ls_color_init(). Start search with
2905 2899 * most specific color rule and work towards most general.
2906 2900 */
2907 2901 for (i = lsc_ncolors - 1; i >= 0; --i)
2908 2902 if (color_match(fname, mode, &lsc_colors[i]))
2909 2903 return (&lsc_colors[i]);
2910 2904
2911 2905 return (NULL);
2912 2906 }
2913 2907
2914 2908 static void
2915 2909 ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4,
2916 2910 long int p5, long int p6, long int p7, long int p8, long int p9)
2917 2911 {
2918 2912 char *s;
2919 2913
2920 2914 if (str == NULL)
2921 2915 return;
2922 2916
2923 2917 s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
2924 2918
2925 2919 if (s != NULL)
2926 2920 (void) putp(s);
2927 2921 }
2928 2922
2929 2923 static void
2930 2924 ls_start_color(ls_color_t *c)
2931 2925 {
2932 2926 if (c == NULL)
2933 2927 return;
2934 2928
2935 2929 if (lsc_debug)
2936 2930 lsc_match = c;
2937 2931
2938 2932 if (c->attr & LSA_BOLD)
2939 2933 ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2940 2934 if (c->attr & LSA_UNDERSCORE)
2941 2935 ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2942 2936 if (c->attr & LSA_BLINK)
2943 2937 ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2944 2938 if (c->attr & LSA_REVERSE)
2945 2939 ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2946 2940 if (c->attr & LSA_CONCEALED)
2947 2941 ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2948 2942 if (c->attr == LSA_NONE)
2949 2943 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2950 2944
2951 2945 if (c->fg != -1)
2952 2946 ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0);
2953 2947 if (c->bg != -1)
2954 2948 ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0);
2955 2949 }
2956 2950
2957 2951 static void
2958 2952 ls_end_color()
2959 2953 {
2960 2954 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2961 2955 if (lsc_debug)
2962 2956 dump_color(lsc_match);
2963 2957 }
2964 2958
2965 2959 static void
2966 2960 new_color_entry(char *colorstr)
2967 2961 {
2968 2962 static const struct {
2969 2963 const char *s;
2970 2964 ls_cftype_t stype;
2971 2965 } type_map[] = {
2972 2966 { "no", LS_NORMAL },
2973 2967 { "fi", LS_FILE },
2974 2968 { "di", LS_DIR },
2975 2969 { "ln", LS_LINK },
2976 2970 { "pi", LS_FIFO },
2977 2971 { "so", LS_SOCK },
2978 2972 { "do", LS_DOOR },
2979 2973 { "bd", LS_BLK },
2980 2974 { "cd", LS_CHR },
2981 2975 { "or", LS_ORPHAN },
2982 2976 { "su", LS_SETUID },
2983 2977 { "sg", LS_SETGID },
2984 2978 { "tw", LS_STICKY_OTHER_WRITABLE },
2985 2979 { "ow", LS_OTHER_WRITABLE },
2986 2980 { "st", LS_STICKY },
2987 2981 { "ex", LS_EXEC },
2988 2982 { "po", LS_PORT },
2989 2983 { NULL, LS_NORMAL }
2990 2984 };
2991 2985
2992 2986 char *p, *lasts;
2993 2987 int i;
2994 2988 int color, attr;
2995 2989
2996 2990 p = strtok_r(colorstr, "=", &lasts);
2997 2991 if (p == NULL) {
2998 2992 colorflg = 0;
2999 2993 return;
3000 2994 }
3001 2995
3002 2996 if (p[0] == '*') {
3003 2997 lsc_colors[lsc_ncolors].ftype = LS_PAT;
3004 2998 /* don't include the * in the suffix */
3005 2999 if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) {
3006 3000 colorflg = 0;
3007 3001 return;
3008 3002 }
3009 3003 } else {
3010 3004 lsc_colors[lsc_ncolors].sfx = NULL;
3011 3005
3012 3006 for (i = 0; type_map[i].s != NULL; ++i) {
3013 3007 if (strncmp(type_map[i].s, p, 2) == 0)
3014 3008 break;
3015 3009 }
3016 3010
3017 3011 /* ignore unknown file types */
3018 3012 if (type_map[i].s == NULL)
3019 3013 return;
3020 3014
3021 3015 lsc_colors[lsc_ncolors].ftype = type_map[i].stype;
3022 3016 }
3023 3017
3024 3018 attr = LSA_NONE;
3025 3019 lsc_colors[lsc_ncolors].fg = -1;
3026 3020 lsc_colors[lsc_ncolors].bg = -1;
3027 3021 for (p = strtok_r(NULL, ";", &lasts); p != NULL;
3028 3022 p = strtok_r(NULL, ";", &lasts)) {
3029 3023 color = strtol(p, NULL, 10);
3030 3024
3031 3025 if (color < 10) {
3032 3026 switch (color) {
3033 3027 case 0:
3034 3028 attr = LSA_NONE;
3035 3029 continue;
3036 3030 case 1:
3037 3031 attr |= LSA_BOLD;
3038 3032 continue;
3039 3033 case 4:
3040 3034 attr |= LSA_UNDERSCORE;
3041 3035 continue;
3042 3036 case 5:
3043 3037 attr |= LSA_BLINK;
3044 3038 continue;
3045 3039 case 7:
3046 3040 attr |= LSA_REVERSE;
3047 3041 continue;
3048 3042 case 8:
3049 3043 attr |= LSA_CONCEALED;
3050 3044 continue;
3051 3045 default:
3052 3046 continue;
3053 3047 }
3054 3048 }
3055 3049
3056 3050 if (color < 40)
3057 3051 lsc_colors[lsc_ncolors].fg = color - 30;
3058 3052 else
3059 3053 lsc_colors[lsc_ncolors].bg = color - 40;
3060 3054 }
3061 3055
3062 3056 lsc_colors[lsc_ncolors].attr = attr;
3063 3057 ++lsc_ncolors;
3064 3058 }
3065 3059
3066 3060 static int
3067 3061 ls_color_compare(const void *p1, const void *p2)
3068 3062 {
3069 3063 const ls_color_t *c1 = (const ls_color_t *)p1;
3070 3064 const ls_color_t *c2 = (const ls_color_t *)p2;
3071 3065
3072 3066 int ret = c1->ftype - c2->ftype;
3073 3067
3074 3068 if (ret != 0)
3075 3069 return (ret);
3076 3070
3077 3071 if (c1->ftype != LS_PAT)
3078 3072 return (ret);
3079 3073
3080 3074 return (strcmp(c1->sfx, c2->sfx));
3081 3075 }
3082 3076
3083 3077 static void
3084 3078 ls_color_init()
3085 3079 {
3086 3080 static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
3087 3081 ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
3088 3082 ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
3089 3083 ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
3090 3084 ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
3091 3085 ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
3092 3086 ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
3093 3087 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3094 3088 ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3095 3089 ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3096 3090 ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3097 3091
3098 3092 char *colorstr;
3099 3093 char *p, *lasts;
3100 3094 size_t color_sz;
3101 3095 int termret;
3102 3096 int i;
3103 3097
3104 3098 (void) setupterm(NULL, 1, &termret);
3105 3099 if (termret != 1)
3106 3100 return;
3107 3101
3108 3102 if ((p = getenv("LS_COLORS")) == NULL)
3109 3103 p = default_colorstr;
3110 3104 colorstr = strdup(p);
3111 3105 if (colorstr == NULL)
3112 3106 return;
3113 3107
3114 3108 /*
3115 3109 * Determine the size of lsc_colors. color_sz can be > lsc_ncolors
3116 3110 * if there are invalid entries passed in the string (they are ignored)
3117 3111 */
3118 3112 color_sz = 1;
3119 3113 for (p = strchr(colorstr, ':'); p != NULL && *p != '\0';
3120 3114 p = strchr(++p, ':'))
3121 3115 ++color_sz;
3122 3116
3123 3117 lsc_colors = calloc(color_sz, sizeof (ls_color_t));
3124 3118 if (lsc_colors == NULL) {
3125 3119 free(colorstr);
3126 3120 return;
3127 3121 }
3128 3122
3129 3123 for (p = strtok_r(colorstr, ":", &lasts);
3130 3124 p != NULL && lsc_ncolors < color_sz;
3131 3125 p = strtok_r(NULL, ":", &lasts))
3132 3126 new_color_entry(p);
3133 3127
3134 3128 qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t),
3135 3129 ls_color_compare);
3136 3130
3137 3131 for (i = 0; i < lsc_ncolors; ++i)
3138 3132 if (lsc_colors[i].ftype == LS_ORPHAN) {
3139 3133 lsc_orphan = &lsc_colors[i];
3140 3134 break;
3141 3135 }
3142 3136
3143 3137 if ((lsc_bold = tigetstr("bold")) == (char *)-1)
3144 3138 lsc_bold = NULL;
3145 3139
3146 3140 if ((lsc_underline = tigetstr("smul")) == (char *)-1)
3147 3141 lsc_underline = NULL;
3148 3142
3149 3143 if ((lsc_blink = tigetstr("blink")) == (char *)-1)
3150 3144 lsc_blink = NULL;
3151 3145
3152 3146 if ((lsc_reverse = tigetstr("rev")) == (char *)-1)
3153 3147 lsc_reverse = NULL;
3154 3148
3155 3149 if ((lsc_concealed = tigetstr("prot")) == (char *)-1)
3156 3150 lsc_concealed = NULL;
3157 3151
3158 3152 if ((lsc_none = tigetstr("sgr0")) == (char *)-1)
3159 3153 lsc_none = NULL;
3160 3154
3161 3155 if ((lsc_setfg = tigetstr("setaf")) == (char *)-1)
3162 3156 lsc_setfg = NULL;
3163 3157
3164 3158 if ((lsc_setbg = tigetstr("setab")) == (char *)-1)
3165 3159 lsc_setbg = NULL;
3166 3160
3167 3161 if (getenv("_LS_COLOR_DEBUG") != NULL) {
3168 3162 int i;
3169 3163
3170 3164 lsc_debug = 1;
3171 3165 for (i = 0; i < lsc_ncolors; ++i)
3172 3166 dump_color(&lsc_colors[i]);
3173 3167 }
3174 3168
3175 3169 free(colorstr);
3176 3170 }
3177 3171
3178 3172 /* Free extended system attribute lists */
3179 3173
3180 3174 void
3181 3175 free_sysattr(struct lbuf *p)
3182 3176 {
3183 3177 int i;
3184 3178
3185 3179 if (p->exttr != NULL) {
3186 3180 for (i = 0; i < sacnt; i++) {
3187 3181 if (p->exttr[i].name != NULL)
3188 3182 free(p->exttr[i].name);
3189 3183 }
3190 3184 free(p->exttr);
3191 3185 }
3192 3186 if (p->extm != NULL) {
3193 3187 for (i = 0; i < sacnt; i++) {
3194 3188 if (p->extm[i].name != NULL)
3195 3189 free(p->extm[i].name);
3196 3190 }
3197 3191 free(p->extm);
3198 3192 }
3199 3193 }
3200 3194
3201 3195 /* Allocate extended system attribute list */
3202 3196
3203 3197 void *
3204 3198 xmalloc(size_t size, struct lbuf *p)
3205 3199 {
3206 3200 if ((p = malloc(size)) == NULL) {
3207 3201 perror("ls");
3208 3202 free_sysattr(p);
3209 3203 nvlist_free(response);
3210 3204 exit(2);
3211 3205 }
3212 3206 return (p);
3213 3207 }
↓ open down ↓ |
1366 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX