1 /* Portions Copyright 2006 Stephen P. Potter */
4 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
8 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
9 /* All Rights Reserved */
12 * Copyright (c) 1980 Regents of the University of California.
13 * All rights reserved. The Berkeley software License Agreement
14 * specifies the terms and conditions for redistribution.
20 * 4.2bsd version for symbolic links, variable length
21 * directory entries, block size in the inode, etc.
35 #include <sys/types.h>
36 #include <sys/param.h>
38 #include <sys/termios.h>
39 #include <sys/mkdev.h>
42 #define dbtokb(nb) ((nb) / (1024 / DEV_BSIZE))
45 char ftype
; /* file type, e.g. 'd', 'c', 'f' */
46 ino_t fnum
; /* inode number of file */
47 short fflags
; /* mode&~S_IFMT, perhaps ISARG */
48 nlink_t fnl
; /* number of links */
49 uid_t fuid
; /* owner id */
50 gid_t fgid
; /* group id */
51 off_t fsize
; /* file size */
52 blkcnt_t fblks
; /* number of blocks used */
53 time_t fmtime
; /* time (modify or access or create) */
54 char *fname
; /* file name */
55 char *flinkto
; /* symbolic link value */
56 char acl
; /* acl access flag */
59 #define ISARG 0x8000 /* extra ``mode'' */
61 static struct subdirs
{
63 struct subdirs
*sd_next
;
66 static int aflg
, dflg
, gflg
, lflg
, sflg
, tflg
, uflg
, iflg
, fflg
, cflg
;
68 static int qflg
, Aflg
, Cflg
, Fflg
, Lflg
, Rflg
;
72 static time_t now
, sixmonthsago
, onehourfromnow
;
74 static char *dotp
= ".";
76 static struct winsize win
;
79 static struct afile
*gstat(struct afile
*, char *, int, off_t
*);
80 static int fcmp(const void *, const void *);
81 static char *cat(char *, char *);
82 static char *savestr(char *);
83 static char *fmtentry(struct afile
*);
84 static char *getname(), *getgroup();
85 static void formatd(char *, int);
86 static void formatf(struct afile
*, struct afile
*);
87 static off_t
getdir(char *, struct afile
**, struct afile
**);
90 main(int argc
, char **argv
)
93 struct afile
*fp0
, *fplast
;
94 register struct afile
*fp
;
101 sixmonthsago
= now
- 6L*30L*24L*60L*60L;
102 onehourfromnow
= now
+ 60L*60L;
107 (void) ioctl(1, TCGETS
, &trbuf
);
108 if (ioctl(1, TIOCGWINSZ
, &win
) != -1)
109 twidth
= (win
.ws_col
== 0 ? 80 : win
.ws_col
);
110 if ((trbuf
.c_oflag
& TABDLY
) != TAB3
)
115 (void) setlocale(LC_ALL
, ""); /* set local environment */
117 while (argc
> 0 && **argv
== '-') {
120 switch (*(*argv
)++) {
162 aflg
++; lflg
= 0; sflg
= 0; tflg
= 0;
170 fp
= (struct afile
*)calloc(argc
, sizeof (struct afile
));
172 (void) fprintf(stderr
, "ls: out of memory\n");
176 for (i
= 0; i
< argc
; i
++) {
177 if (gstat(fp
, *argv
, 1, (off_t
*)0)) {
185 qsort(fp0
, fplast
- fp0
, sizeof (struct afile
), fcmp
);
187 formatf(fp0
, fplast
);
193 for (fp
= fp0
; fp
< fplast
&& fp
->ftype
!= 'd'; fp
++)
201 formatd(fp
->fname
, argc
> 1);
205 t
= subdirs
; subdirs
= t
->sd_next
;
207 formatd(t
->sd_name
, 1);
220 formatd(char *name
, int title
)
222 register struct afile
*fp
;
223 register struct subdirs
*dp
;
224 struct afile
*dfp0
, *dfplast
;
227 nkb
= getdir(name
, &dfp0
, &dfplast
);
231 qsort(dfp0
, dfplast
- dfp0
, sizeof (struct afile
), fcmp
);
233 (void) printf("%s:\n", name
);
235 (void) printf("total %lld\n", nkb
);
236 formatf(dfp0
, dfplast
);
238 for (fp
= dfplast
- 1; fp
>= dfp0
; fp
--) {
239 if (fp
->ftype
!= 'd' ||
240 strcmp(fp
->fname
, ".") == 0 ||
241 strcmp(fp
->fname
, "..") == 0)
243 dp
= (struct subdirs
*)malloc(sizeof (struct subdirs
));
244 dp
->sd_name
= savestr(cat(name
, fp
->fname
));
245 dp
->sd_next
= subdirs
; subdirs
= dp
;
247 for (fp
= dfp0
; fp
< dfplast
; fp
++) {
248 if ((fp
->fflags
&ISARG
) == 0 && fp
->fname
)
257 getdir(char *dir
, struct afile
**pfp0
, struct afile
**pfplast
)
259 register struct afile
*fp
;
261 register struct dirent
*dp
;
266 * This code (opendir, readdir, and the "for" loop) is arranged in
267 * this strange manner to handle the case where UNIX lets root open
268 * any directory for reading, but NFS does not let root read the
271 *pfp0
= *pfplast
= NULL
;
272 if ((dirp
= opendir(dir
)) == NULL
) {
273 (void) printf("%s unreadable\n", dir
); /* not stderr! */
277 if (((dp
= readdir(dirp
)) == NULL
) && (errno
!= 0)) {
278 /* root reading across NFS can get to this error case */
279 (void) printf("%s unreadable\n", dir
); /* not stderr! */
280 (void) closedir(dirp
);
283 fp
= *pfp0
= (struct afile
*)calloc(nent
, sizeof (struct afile
));
284 *pfplast
= *pfp0
+ nent
;
285 for (nb
= 0; dp
!= NULL
; dp
= readdir(dirp
)) {
288 if (aflg
== 0 && dp
->d_name
[0] == '.' &&
289 (Aflg
== 0 || dp
->d_name
[1] == 0 ||
290 dp
->d_name
[1] == '.' && dp
->d_name
[2] == 0))
292 if (gstat(fp
, cat(dir
, dp
->d_name
), Fflg
+Rflg
, &nb
) == 0)
294 fp
->fnum
= dp
->d_ino
;
295 fp
->fname
= savestr(dp
->d_name
);
297 if (fp
== *pfplast
) {
298 *pfp0
= (struct afile
*)realloc((char *)*pfp0
,
299 2 * nent
* sizeof (struct afile
));
301 (void) fprintf(stderr
, "ls: out of memory\n");
305 *pfplast
= fp
+ nent
;
309 (void) closedir(dirp
);
315 static struct afile
*
316 gstat(struct afile
*fp
, char *file
, int statarg
, off_t
*pnb
)
318 static struct afile azerofile
;
319 int (*statf
)() = Lflg
? stat
: lstat
;
325 o_mode_t groupperm
, mask
;
326 int grouppermfound
, maskfound
;
332 if (statarg
|| sflg
|| lflg
|| tflg
) {
333 struct stat stb
, stb1
;
335 if ((*statf
)(file
, &stb
) < 0) {
336 if (statf
== lstat
|| lstat(file
, &stb
) < 0) {
338 (void) fprintf(stderr
,
339 "%s not found\n", file
);
341 (void) fprintf(stderr
, "ls: ");
347 fp
->fblks
= stb
.st_blocks
;
348 fp
->fsize
= stb
.st_size
;
349 switch (stb
.st_mode
& S_IFMT
) {
351 fp
->ftype
= 'd'; break;
353 fp
->ftype
= 'D'; break;
355 fp
->ftype
= 'b'; fp
->fsize
= (off_t
)stb
.st_rdev
; break;
357 fp
->ftype
= 'c'; fp
->fsize
= (off_t
)stb
.st_rdev
; break;
359 fp
->ftype
= 's'; fp
->fsize
= 0LL; break;
361 fp
->ftype
= 'p'; fp
->fsize
= 0LL; break;
365 cc
= readlink(file
, buf
, BUFSIZ
);
368 * here we follow the symbolic
369 * link to generate the proper
370 * Fflg marker for the object,
371 * eg, /bin -> /pub/bin/
374 if (Fflg
&& !stat(file
, &stb1
))
375 switch (stb1
.st_mode
& S_IFMT
) {
395 fp
->flinkto
= savestr(buf
);
400 * this is a hack from UCB to avoid having
401 * ls /bin behave differently from ls /bin/
402 * when /bin is a symbolic link. We hack the
403 * hack to have that happen, but only for
404 * explicit arguments, by inspecting pnb.
406 if (pnb
!= (off_t
*)0 || stat(file
, &stb1
) < 0)
408 if ((stb1
.st_mode
& S_IFMT
) == S_IFDIR
) {
411 fp
->fsize
= stb
.st_size
;
412 fp
->fblks
= stb
.st_blocks
;
416 fp
->fnum
= stb
.st_ino
;
417 fp
->fflags
= stb
.st_mode
& ~S_IFMT
;
418 fp
->fnl
= stb
.st_nlink
;
419 fp
->fuid
= stb
.st_uid
;
420 fp
->fgid
= stb
.st_gid
;
422 /* ACL: check acl entries count */
423 if ((aclcnt
= acl(file
, GETACLCNT
, 0, NULL
)) >
426 /* this file has a non-trivial acl */
431 * For files with non-trivial acls, the
432 * effective group permissions are the
433 * intersection of the GROUP_OBJ value and
434 * the CLASS_OBJ (acl mask) value. Determine
435 * both the GROUP_OBJ and CLASS_OBJ for this
436 * file and insert the logical AND of those
437 * two values in the group permissions field
438 * of the lflags value for this file.
441 if ((aclp
= (aclent_t
*)malloc(
442 (sizeof (aclent_t
)) * aclcnt
)) == NULL
) {
447 if (acl(file
, GETACL
, aclcnt
, aclp
) < 0) {
449 (void) fprintf(stderr
, "ls: ");
455 * Until found in acl list, assume maximum
456 * permissions for both group and mask. (Just
457 * in case the acl lacks either value for
464 for (tp
= aclp
; aclcnt
--; tp
++) {
465 if (tp
->a_type
== GROUP_OBJ
) {
466 groupperm
= tp
->a_perm
;
470 if (tp
->a_type
== CLASS_OBJ
) {
474 if (grouppermfound
&& maskfound
)
480 /* reset all the group bits */
481 fp
->fflags
&= ~S_IRWXG
;
484 * Now set them to the logical AND of the
485 * GROUP_OBJ permissions and the acl mask.
488 fp
->fflags
|= (groupperm
& mask
) << 3;
493 fp
->fmtime
= stb
.st_atime
;
495 fp
->fmtime
= stb
.st_ctime
;
497 fp
->fmtime
= stb
.st_mtime
;
499 *pnb
+= stb
.st_blocks
;
505 formatf(struct afile
*fp0
, struct afile
*fplast
)
507 register struct afile
*fp
;
508 int width
= 0, w
, nentry
= fplast
- fp0
;
509 int i
, j
, columns
, lines
;
514 if (lflg
|| Cflg
== 0)
517 for (fp
= fp0
; fp
< fplast
; fp
++) {
518 int len
= strlen(fmtentry(fp
));
524 width
= (width
+ 8) &~ 7;
527 columns
= twidth
/ width
;
531 lines
= (nentry
+ columns
- 1) / columns
;
532 for (i
= 0; i
< lines
; i
++) {
533 for (j
= 0; j
< columns
; j
++) {
534 fp
= fp0
+ j
* lines
+ i
;
536 (void) printf("%s", cp
);
537 if (fp
+ lines
>= fplast
) {
545 (void) putchar('\t');
555 fcmp(const void *arg1
, const void *arg2
)
557 const struct afile
*f1
= arg1
;
558 const struct afile
*f2
= arg2
;
560 if (dflg
== 0 && fflg
== 0) {
561 if ((f1
->fflags
&ISARG
) && f1
->ftype
== 'd') {
562 if ((f2
->fflags
&ISARG
) == 0 || f2
->ftype
!= 'd')
565 if ((f2
->fflags
&ISARG
) && f2
->ftype
== 'd')
570 if (f2
->fmtime
== f1
->fmtime
)
572 if (f2
->fmtime
> f1
->fmtime
)
576 return (rflg
* strcmp(f1
->fname
, f2
->fname
));
580 cat(char *dir
, char *file
)
582 static char dfile
[BUFSIZ
];
584 if (strlen(dir
)+1+strlen(file
)+1 > BUFSIZ
) {
585 (void) fprintf(stderr
, "ls: filename too long\n");
588 if (strcmp(dir
, "") == 0 || strcmp(dir
, ".") == 0) {
589 (void) strcpy(dfile
, file
);
592 (void) strcpy(dfile
, dir
);
593 if (dir
[strlen(dir
) - 1] != '/' && *file
!= '/')
594 (void) strcat(dfile
, "/");
595 (void) strcat(dfile
, file
);
602 char *cp
= malloc(strlen(str
) + 1);
605 (void) fprintf(stderr
, "ls: out of memory\n");
608 (void) strcpy(cp
, str
);
612 static char *fmtinum(struct afile
*);
613 static char *fmtsize(struct afile
*);
614 static char *fmtlstuff(struct afile
*);
615 static char *fmtmode(char *, int);
618 fmtentry(struct afile
*fp
)
620 static char fmtres
[BUFSIZ
];
621 register char *cp
, *dp
;
623 (void) sprintf(fmtres
, "%s%s%s",
624 iflg
? fmtinum(fp
) : "",
625 sflg
? fmtsize(fp
) : "",
626 lflg
? fmtlstuff(fp
) : "");
627 dp
= &fmtres
[strlen(fmtres
)];
628 for (cp
= fp
->fname
; *cp
; cp
++)
629 if (qflg
&& !isprint((unsigned char)*cp
))
633 /* avoid both "->" and trailing marks */
634 if (Fflg
&& ! (lflg
&& fp
->flinkto
)) {
635 if (fp
->ftype
== 'd')
637 else if (fp
->ftype
== 'D')
639 else if (fp
->ftype
== 'p')
641 else if (fp
->ftype
== 'l')
643 else if (fp
->ftype
== 's')
645 else if (fp
->fflags
& 0111)
648 if (lflg
&& fp
->flinkto
) {
649 (void) strcpy(dp
, " -> "); dp
+= 4;
650 for (cp
= fp
->flinkto
; *cp
; cp
++)
651 if (qflg
&& !isprint((unsigned char) *cp
))
661 fmtinum(struct afile
*p
)
663 static char inumbuf
[12];
665 (void) sprintf(inumbuf
, "%10llu ", p
->fnum
);
670 fmtsize(struct afile
*p
)
672 static char sizebuf
[32];
674 (void) sprintf(sizebuf
, (off_t
)dbtokb(p
->fblks
) < 10000 ? "%4lld " : \
675 "%lld ", (off_t
)dbtokb(p
->fblks
));
680 fmtlstuff(struct afile
*p
)
682 static char lstuffbuf
[256];
683 char gname
[32], uname
[32], fsize
[32], ftime
[32];
684 register char *lp
= lstuffbuf
;
686 /* type mode uname gname fsize ftime */
689 char *cp
= getname(p
->fuid
);
690 (void) sprintf(uname
, "%-8s ", cp
);
694 char *cp
= getgroup(p
->fgid
);
695 (void) sprintf(gname
, "%-8s ", cp
);
698 if (p
->ftype
== 'b' || p
->ftype
== 'c')
699 (void) sprintf(fsize
, "%3ld,%4ld",
700 major(p
->fsize
), minor(p
->fsize
));
701 else if (p
->ftype
== 's')
702 (void) sprintf(fsize
, "%8d", 0);
704 (void) sprintf(fsize
, p
->fsize
< 100000000 ? "%8lld" : \
708 char *cp
= ctime(&p
->fmtime
);
709 if ((p
->fmtime
< sixmonthsago
) || (p
->fmtime
> onehourfromnow
))
710 (void) sprintf(ftime
, " %-7.7s %-4.4s ", cp
+4, cp
+20);
712 (void) sprintf(ftime
, " %-12.12s ", cp
+4);
716 lp
= fmtmode(lp
, p
->fflags
);
717 (void) sprintf(lp
, "%c%3ld %s%s%s%s",
718 p
->acl
, p
->fnl
, uname
, gflg
? gname
: "", fsize
, ftime
);
723 { 1, S_IREAD
>>0, 'r', '-' };
725 { 1, S_IWRITE
>>0, 'w', '-' };
727 { 3, S_ISUID
|(S_IEXEC
>>0), 's', S_IEXEC
>>0, 'x', S_ISUID
, 'S', '-' };
729 { 1, S_IREAD
>>3, 'r', '-' };
731 { 1, S_IWRITE
>>3, 'w', '-' };
733 { 3, S_ISGID
|(S_IEXEC
>>3), 's', S_IEXEC
>>3, 'x', S_ISGID
, 'S', '-' };
735 { 1, S_IREAD
>>6, 'r', '-' };
737 { 1, S_IWRITE
>>6, 'w', '-' };
739 { 3, S_ISVTX
|(S_IEXEC
>>6), 't', S_ISVTX
, 'T', S_IEXEC
>>6, 'x', '-' };
741 static int *m
[] = { m1
, m2
, m3
, m4
, m5
, m6
, m7
, m8
, m9
};
744 fmtmode(char *lp
, int flags
)
748 for (mp
= &m
[0]; mp
< &m
[sizeof (m
)/sizeof (m
[0])]; ) {
749 register int *pairp
= *mp
++;
750 register int n
= *pairp
++;
753 if ((flags
&*pairp
) == *pairp
) {
764 /* rest should be done with nameserver or database */
770 #define NMAX (sizeof (((struct utmpx *)0)->ut_name))
771 #define SCPYN(a, b) strncpy(a, b, NMAX)
774 static struct cachenode
{ /* this struct must be zeroed before using */
775 struct cachenode
*lesschild
; /* subtree whose entries < val */
776 struct cachenode
*grtrchild
; /* subtree whose entries > val */
777 int val
; /* the uid or gid of this entry */
778 int initted
; /* name has been filled in */
779 char name
[NMAX
+1]; /* the string that val maps to */
782 static struct cachenode
*
783 findincache(struct cachenode
**head
, id_t val
)
785 register struct cachenode
**parent
= head
;
786 register struct cachenode
*c
= *parent
;
792 } else if (val
< c
->val
) {
793 parent
= &c
->lesschild
;
796 parent
= &c
->grtrchild
;
801 /* not in the cache, make a new entry for it */
802 *parent
= c
= (struct cachenode
*)calloc(1, sizeof (struct cachenode
));
813 c
= findincache(&names
, uid
);
814 if (c
->initted
== 0) {
815 if ((pw
= getpwuid(uid
)) != NULL
) {
816 (void) SCPYN(&c
->name
[0], pw
->pw_name
);
818 (void) sprintf(&c
->name
[0], "%-8lu ", uid
);
822 return (&c
->name
[0]);
831 c
= findincache(&groups
, gid
);
832 if (c
->initted
== 0) {
833 if ((gr
= getgrgid(gid
)) != NULL
) {
834 (void) SCPYN(&c
->name
[0], gr
->gr_name
);
836 (void) sprintf(&c
->name
[0], "%-8lu ", gid
);
840 return (&c
->name
[0]);