1 /* Portions Copyright 2006 Stephen P. Potter */
2
3 /*
4 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
6 */
7
8 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
9 /* All Rights Reserved */
10
11 /*
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.
15 */
16
17 #pragma ident "%Z%%M% %I% %E% SMI"
18
19 /*
20 * ls
21 *
22 * 4.2bsd version for symbolic links, variable length
23 * directory entries, block size in the inode, etc.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stddef.h>
31 #include <dirent.h>
32 #include <ctype.h>
33 #include <time.h>
34 #include <limits.h>
35 #include <locale.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <sys/termios.h>
41 #include <sys/mkdev.h>
42 #include <sys/acl.h>
43
44 #define dbtokb(nb) ((nb) / (1024 / DEV_BSIZE))
45
46 struct afile {
47 char ftype; /* file type, e.g. 'd', 'c', 'f' */
48 ino_t fnum; /* inode number of file */
49 short fflags; /* mode&~S_IFMT, perhaps ISARG */
50 nlink_t fnl; /* number of links */
51 uid_t fuid; /* owner id */
52 gid_t fgid; /* group id */
53 off_t fsize; /* file size */
54 blkcnt_t fblks; /* number of blocks used */
55 time_t fmtime; /* time (modify or access or create) */
56 char *fname; /* file name */
57 char *flinkto; /* symbolic link value */
58 char acl; /* acl access flag */
59 };
60
61 #define ISARG 0x8000 /* extra ``mode'' */
62
63 static struct subdirs {
64 char *sd_name;
65 struct subdirs *sd_next;
66 } *subdirs;
67
68 static int aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg;
69 static int rflg = 1;
70 static int qflg, Aflg, Cflg, Fflg, Lflg, Rflg;
71
72 static int usetabs;
73
74 static time_t now, sixmonthsago, onehourfromnow;
75
76 static char *dotp = ".";
77
78 static struct winsize win;
79 static int twidth;
80
81 static struct afile *gstat(struct afile *, char *, int, off_t *);
82 static int fcmp(const void *, const void *);
83 static char *cat(char *, char *);
84 static char *savestr(char *);
85 static char *fmtentry(struct afile *);
86 static char *getname(), *getgroup();
87 static void formatd(char *, int);
88 static void formatf(struct afile *, struct afile *);
89 static off_t getdir(char *, struct afile **, struct afile **);
90
91 int
main(int argc,char ** argv)92 main(int argc, char **argv)
93 {
94 int i;
95 struct afile *fp0, *fplast;
96 register struct afile *fp;
97 struct termios trbuf;
98
99 argc--, argv++;
100 if (getuid() == 0)
101 Aflg++;
102 (void) time(&now);
103 sixmonthsago = now - 6L*30L*24L*60L*60L;
104 onehourfromnow = now + 60L*60L;
105 now += 60;
106 twidth = 80;
107 if (isatty(1)) {
108 qflg = Cflg = 1;
109 (void) ioctl(1, TCGETS, &trbuf);
110 if (ioctl(1, TIOCGWINSZ, &win) != -1)
111 twidth = (win.ws_col == 0 ? 80 : win.ws_col);
112 if ((trbuf.c_oflag & TABDLY) != TAB3)
113 usetabs = 1;
114 } else
115 usetabs = 1;
116
117 (void) setlocale(LC_ALL, ""); /* set local environment */
118
119 while (argc > 0 && **argv == '-') {
120 (*argv)++;
121 while (**argv) {
122 switch (*(*argv)++) {
123 case 'C':
124 Cflg = 1; break;
125 case 'q':
126 qflg = 1; break;
127 case '1':
128 Cflg = 0; break;
129 case 'a':
130 aflg++; break;
131 case 'A':
132 Aflg++; break;
133 case 'c':
134 cflg++; break;
135 case 's':
136 sflg++; break;
137 case 'd':
138 dflg++; break;
139 case 'g':
140 gflg++; break;
141 case 'l':
142 lflg++; break;
143 case 'r':
144 rflg = -1; break;
145 case 't':
146 tflg++; break;
147 case 'u':
148 uflg++; break;
149 case 'i':
150 iflg++; break;
151 case 'f':
152 fflg++; break;
153 case 'L':
154 Lflg++; break;
155 case 'F':
156 Fflg++; break;
157 case 'R':
158 Rflg++; break;
159 }
160 }
161 argc--, argv++;
162 }
163 if (fflg) {
164 aflg++; lflg = 0; sflg = 0; tflg = 0;
165 }
166 if (lflg)
167 Cflg = 0;
168 if (argc == 0) {
169 argc++;
170 argv = &dotp;
171 }
172 fp = (struct afile *)calloc(argc, sizeof (struct afile));
173 if (fp == 0) {
174 (void) fprintf(stderr, "ls: out of memory\n");
175 exit(1);
176 }
177 fp0 = fp;
178 for (i = 0; i < argc; i++) {
179 if (gstat(fp, *argv, 1, (off_t *)0)) {
180 fp->fname = *argv;
181 fp->fflags |= ISARG;
182 fp++;
183 }
184 argv++;
185 }
186 fplast = fp;
187 qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp);
188 if (dflg) {
189 formatf(fp0, fplast);
190 exit(0);
191 }
192 if (fflg)
193 fp = fp0;
194 else {
195 for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++)
196 continue;
197 formatf(fp0, fp);
198 }
199 if (fp < fplast) {
200 if (fp > fp0)
201 (void) printf("\n");
202 for (;;) {
203 formatd(fp->fname, argc > 1);
204 while (subdirs) {
205 struct subdirs *t;
206
207 t = subdirs; subdirs = t->sd_next;
208 (void) printf("\n");
209 formatd(t->sd_name, 1);
210 free(t->sd_name);
211 free(t);
212 }
213 if (++fp == fplast)
214 break;
215 (void) printf("\n");
216 }
217 }
218 return (0);
219 }
220
221 static void
formatd(char * name,int title)222 formatd(char *name, int title)
223 {
224 register struct afile *fp;
225 register struct subdirs *dp;
226 struct afile *dfp0, *dfplast;
227 off_t nkb;
228
229 nkb = getdir(name, &dfp0, &dfplast);
230 if (dfp0 == 0)
231 return;
232 if (fflg == 0)
233 qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp);
234 if (title)
235 (void) printf("%s:\n", name);
236 if (lflg || sflg)
237 (void) printf("total %lld\n", nkb);
238 formatf(dfp0, dfplast);
239 if (Rflg)
240 for (fp = dfplast - 1; fp >= dfp0; fp--) {
241 if (fp->ftype != 'd' ||
242 strcmp(fp->fname, ".") == 0 ||
243 strcmp(fp->fname, "..") == 0)
244 continue;
245 dp = (struct subdirs *)malloc(sizeof (struct subdirs));
246 dp->sd_name = savestr(cat(name, fp->fname));
247 dp->sd_next = subdirs; subdirs = dp;
248 }
249 for (fp = dfp0; fp < dfplast; fp++) {
250 if ((fp->fflags&ISARG) == 0 && fp->fname)
251 free(fp->fname);
252 if (fp->flinkto)
253 free(fp->flinkto);
254 }
255 free(dfp0);
256 }
257
258 static off_t
getdir(char * dir,struct afile ** pfp0,struct afile ** pfplast)259 getdir(char *dir, struct afile **pfp0, struct afile **pfplast)
260 {
261 register struct afile *fp;
262 DIR *dirp;
263 register struct dirent *dp;
264 off_t nb;
265 size_t nent = 20;
266
267 /*
268 * This code (opendir, readdir, and the "for" loop) is arranged in
269 * this strange manner to handle the case where UNIX lets root open
270 * any directory for reading, but NFS does not let root read the
271 * openned directory.
272 */
273 *pfp0 = *pfplast = NULL;
274 if ((dirp = opendir(dir)) == NULL) {
275 (void) printf("%s unreadable\n", dir); /* not stderr! */
276 return (0);
277 }
278 errno = 0;
279 if (((dp = readdir(dirp)) == NULL) && (errno != 0)) {
280 /* root reading across NFS can get to this error case */
281 (void) printf("%s unreadable\n", dir); /* not stderr! */
282 (void) closedir(dirp);
283 return (0);
284 }
285 fp = *pfp0 = (struct afile *)calloc(nent, sizeof (struct afile));
286 *pfplast = *pfp0 + nent;
287 for (nb = 0; dp != NULL; dp = readdir(dirp)) {
288 if (dp->d_ino == 0)
289 continue;
290 if (aflg == 0 && dp->d_name[0] == '.' &&
291 (Aflg == 0 || dp->d_name[1] == 0 ||
292 dp->d_name[1] == '.' && dp->d_name[2] == 0))
293 continue;
294 if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0)
295 continue;
296 fp->fnum = dp->d_ino;
297 fp->fname = savestr(dp->d_name);
298 fp++;
299 if (fp == *pfplast) {
300 *pfp0 = (struct afile *)realloc((char *)*pfp0,
301 2 * nent * sizeof (struct afile));
302 if (*pfp0 == 0) {
303 (void) fprintf(stderr, "ls: out of memory\n");
304 exit(1);
305 }
306 fp = *pfp0 + nent;
307 *pfplast = fp + nent;
308 nent *= 2;
309 }
310 }
311 (void) closedir(dirp);
312 *pfplast = fp;
313 return (dbtokb(nb));
314 }
315
316
317 static struct afile *
gstat(struct afile * fp,char * file,int statarg,off_t * pnb)318 gstat(struct afile *fp, char *file, int statarg, off_t *pnb)
319 {
320 static struct afile azerofile;
321 int (*statf)() = Lflg ? stat : lstat;
322 int cc;
323 char buf[PATH_MAX];
324 int aclcnt;
325 aclent_t *aclp;
326 aclent_t *tp;
327 o_mode_t groupperm, mask;
328 int grouppermfound, maskfound;
329
330 *fp = azerofile;
331 fp->fflags = 0;
332 fp->fnum = 0;
333 fp->ftype = '-';
334 if (statarg || sflg || lflg || tflg) {
335 struct stat stb, stb1;
336
337 if ((*statf)(file, &stb) < 0) {
338 if (statf == lstat || lstat(file, &stb) < 0) {
339 if (errno == ENOENT)
340 (void) fprintf(stderr,
341 "%s not found\n", file);
342 else {
343 (void) fprintf(stderr, "ls: ");
344 perror(file);
345 }
346 return (0);
347 }
348 }
349 fp->fblks = stb.st_blocks;
350 fp->fsize = stb.st_size;
351 switch (stb.st_mode & S_IFMT) {
352 case S_IFDIR:
353 fp->ftype = 'd'; break;
354 case S_IFDOOR:
355 fp->ftype = 'D'; break;
356 case S_IFBLK:
357 fp->ftype = 'b'; fp->fsize = (off_t)stb.st_rdev; break;
358 case S_IFCHR:
359 fp->ftype = 'c'; fp->fsize = (off_t)stb.st_rdev; break;
360 case S_IFSOCK:
361 fp->ftype = 's'; fp->fsize = 0LL; break;
362 case S_IFIFO:
363 fp->ftype = 'p'; fp->fsize = 0LL; break;
364 case S_IFLNK:
365 fp->ftype = 'l';
366 if (lflg) {
367 cc = readlink(file, buf, BUFSIZ);
368 if (cc >= 0) {
369 /*
370 * here we follow the symbolic
371 * link to generate the proper
372 * Fflg marker for the object,
373 * eg, /bin -> /pub/bin/
374 */
375 buf[cc] = 0;
376 if (Fflg && !stat(file, &stb1))
377 switch (stb1.st_mode & S_IFMT) {
378 case S_IFDIR:
379 buf[cc++] = '/';
380 break;
381 case S_IFDOOR:
382 buf[cc++] = '>';
383 break;
384 case S_IFIFO:
385 buf[cc++] = '|';
386 break;
387 case S_IFSOCK:
388 buf[cc++] = '=';
389 break;
390 default:
391 if ((stb1.st_mode & ~S_IFMT)
392 & 0111)
393 buf[cc++] = '*';
394 break;
395 }
396 buf[cc] = 0;
397 fp->flinkto = savestr(buf);
398 }
399 break;
400 }
401 /*
402 * this is a hack from UCB to avoid having
403 * ls /bin behave differently from ls /bin/
404 * when /bin is a symbolic link. We hack the
405 * hack to have that happen, but only for
406 * explicit arguments, by inspecting pnb.
407 */
408 if (pnb != (off_t *)0 || stat(file, &stb1) < 0)
409 break;
410 if ((stb1.st_mode & S_IFMT) == S_IFDIR) {
411 stb = stb1;
412 fp->ftype = 'd';
413 fp->fsize = stb.st_size;
414 fp->fblks = stb.st_blocks;
415 }
416 break;
417 }
418 fp->fnum = stb.st_ino;
419 fp->fflags = stb.st_mode & ~S_IFMT;
420 fp->fnl = stb.st_nlink;
421 fp->fuid = stb.st_uid;
422 fp->fgid = stb.st_gid;
423
424 /* ACL: check acl entries count */
425 if ((aclcnt = acl(file, GETACLCNT, 0, NULL)) >
426 MIN_ACL_ENTRIES) {
427
428 /* this file has a non-trivial acl */
429
430 fp->acl = '+';
431
432 /*
433 * For files with non-trivial acls, the
434 * effective group permissions are the
435 * intersection of the GROUP_OBJ value and
436 * the CLASS_OBJ (acl mask) value. Determine
437 * both the GROUP_OBJ and CLASS_OBJ for this
438 * file and insert the logical AND of those
439 * two values in the group permissions field
440 * of the lflags value for this file.
441 */
442
443 if ((aclp = (aclent_t *)malloc(
444 (sizeof (aclent_t)) * aclcnt)) == NULL) {
445 perror("ls");
446 exit(2);
447 }
448
449 if (acl(file, GETACL, aclcnt, aclp) < 0) {
450 free(aclp);
451 (void) fprintf(stderr, "ls: ");
452 perror(file);
453 return (0);
454 }
455
456 /*
457 * Until found in acl list, assume maximum
458 * permissions for both group and mask. (Just
459 * in case the acl lacks either value for
460 * some reason.)
461 */
462 groupperm = 07;
463 mask = 07;
464 grouppermfound = 0;
465 maskfound = 0;
466 for (tp = aclp; aclcnt--; tp++) {
467 if (tp->a_type == GROUP_OBJ) {
468 groupperm = tp->a_perm;
469 grouppermfound = 1;
470 continue;
471 }
472 if (tp->a_type == CLASS_OBJ) {
473 mask = tp->a_perm;
474 maskfound = 1;
475 }
476 if (grouppermfound && maskfound)
477 break;
478 }
479
480 free(aclp);
481
482 /* reset all the group bits */
483 fp->fflags &= ~S_IRWXG;
484
485 /*
486 * Now set them to the logical AND of the
487 * GROUP_OBJ permissions and the acl mask.
488 */
489
490 fp->fflags |= (groupperm & mask) << 3;
491 } else
492 fp->acl = ' ';
493
494 if (uflg)
495 fp->fmtime = stb.st_atime;
496 else if (cflg)
497 fp->fmtime = stb.st_ctime;
498 else
499 fp->fmtime = stb.st_mtime;
500 if (pnb)
501 *pnb += stb.st_blocks;
502 }
503 return (fp);
504 }
505
506 static void
formatf(struct afile * fp0,struct afile * fplast)507 formatf(struct afile *fp0, struct afile *fplast)
508 {
509 register struct afile *fp;
510 int width = 0, w, nentry = fplast - fp0;
511 int i, j, columns, lines;
512 char *cp;
513
514 if (fp0 == fplast)
515 return;
516 if (lflg || Cflg == 0)
517 columns = 1;
518 else {
519 for (fp = fp0; fp < fplast; fp++) {
520 int len = strlen(fmtentry(fp));
521
522 if (len > width)
523 width = len;
524 }
525 if (usetabs)
526 width = (width + 8) &~ 7;
527 else
528 width += 2;
529 columns = twidth / width;
530 if (columns == 0)
531 columns = 1;
532 }
533 lines = (nentry + columns - 1) / columns;
534 for (i = 0; i < lines; i++) {
535 for (j = 0; j < columns; j++) {
536 fp = fp0 + j * lines + i;
537 cp = fmtentry(fp);
538 (void) printf("%s", cp);
539 if (fp + lines >= fplast) {
540 (void) printf("\n");
541 break;
542 }
543 w = strlen(cp);
544 while (w < width)
545 if (usetabs) {
546 w = (w + 8) &~ 7;
547 (void) putchar('\t');
548 } else {
549 w++;
550 (void) putchar(' ');
551 }
552 }
553 }
554 }
555
556 static int
fcmp(const void * arg1,const void * arg2)557 fcmp(const void *arg1, const void *arg2)
558 {
559 const struct afile *f1 = arg1;
560 const struct afile *f2 = arg2;
561
562 if (dflg == 0 && fflg == 0) {
563 if ((f1->fflags&ISARG) && f1->ftype == 'd') {
564 if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd')
565 return (1);
566 } else {
567 if ((f2->fflags&ISARG) && f2->ftype == 'd')
568 return (-1);
569 }
570 }
571 if (tflg) {
572 if (f2->fmtime == f1->fmtime)
573 return (0);
574 if (f2->fmtime > f1->fmtime)
575 return (rflg);
576 return (-rflg);
577 }
578 return (rflg * strcmp(f1->fname, f2->fname));
579 }
580
581 static char *
cat(char * dir,char * file)582 cat(char *dir, char *file)
583 {
584 static char dfile[BUFSIZ];
585
586 if (strlen(dir)+1+strlen(file)+1 > BUFSIZ) {
587 (void) fprintf(stderr, "ls: filename too long\n");
588 exit(1);
589 }
590 if (strcmp(dir, "") == 0 || strcmp(dir, ".") == 0) {
591 (void) strcpy(dfile, file);
592 return (dfile);
593 }
594 (void) strcpy(dfile, dir);
595 if (dir[strlen(dir) - 1] != '/' && *file != '/')
596 (void) strcat(dfile, "/");
597 (void) strcat(dfile, file);
598 return (dfile);
599 }
600
601 static char *
savestr(char * str)602 savestr(char *str)
603 {
604 char *cp = malloc(strlen(str) + 1);
605
606 if (cp == NULL) {
607 (void) fprintf(stderr, "ls: out of memory\n");
608 exit(1);
609 }
610 (void) strcpy(cp, str);
611 return (cp);
612 }
613
614 static char *fmtinum(struct afile *);
615 static char *fmtsize(struct afile *);
616 static char *fmtlstuff(struct afile *);
617 static char *fmtmode(char *, int);
618
619 static char *
fmtentry(struct afile * fp)620 fmtentry(struct afile *fp)
621 {
622 static char fmtres[BUFSIZ];
623 register char *cp, *dp;
624
625 (void) sprintf(fmtres, "%s%s%s",
626 iflg ? fmtinum(fp) : "",
627 sflg ? fmtsize(fp) : "",
628 lflg ? fmtlstuff(fp) : "");
629 dp = &fmtres[strlen(fmtres)];
630 for (cp = fp->fname; *cp; cp++)
631 if (qflg && !isprint((unsigned char)*cp))
632 *dp++ = '?';
633 else
634 *dp++ = *cp;
635 /* avoid both "->" and trailing marks */
636 if (Fflg && ! (lflg && fp->flinkto)) {
637 if (fp->ftype == 'd')
638 *dp++ = '/';
639 else if (fp->ftype == 'D')
640 *dp++ = '>';
641 else if (fp->ftype == 'p')
642 *dp++ = '|';
643 else if (fp->ftype == 'l')
644 *dp++ = '@';
645 else if (fp->ftype == 's')
646 *dp++ = '=';
647 else if (fp->fflags & 0111)
648 *dp++ = '*';
649 }
650 if (lflg && fp->flinkto) {
651 (void) strcpy(dp, " -> "); dp += 4;
652 for (cp = fp->flinkto; *cp; cp++)
653 if (qflg && !isprint((unsigned char) *cp))
654 *dp++ = '?';
655 else
656 *dp++ = *cp;
657 }
658 *dp++ = 0;
659 return (fmtres);
660 }
661
662 static char *
fmtinum(struct afile * p)663 fmtinum(struct afile *p)
664 {
665 static char inumbuf[12];
666
667 (void) sprintf(inumbuf, "%10llu ", p->fnum);
668 return (inumbuf);
669 }
670
671 static char *
fmtsize(struct afile * p)672 fmtsize(struct afile *p)
673 {
674 static char sizebuf[32];
675
676 (void) sprintf(sizebuf, (off_t)dbtokb(p->fblks) < 10000 ? "%4lld " : \
677 "%lld ", (off_t)dbtokb(p->fblks));
678 return (sizebuf);
679 }
680
681 static char *
fmtlstuff(struct afile * p)682 fmtlstuff(struct afile *p)
683 {
684 static char lstuffbuf[256];
685 char gname[32], uname[32], fsize[32], ftime[32];
686 register char *lp = lstuffbuf;
687
688 /* type mode uname gname fsize ftime */
689 /* get uname */
690 {
691 char *cp = getname(p->fuid);
692 (void) sprintf(uname, "%-8s ", cp);
693 }
694 /* get gname */
695 if (gflg) {
696 char *cp = getgroup(p->fgid);
697 (void) sprintf(gname, "%-8s ", cp);
698 }
699 /* get fsize */
700 if (p->ftype == 'b' || p->ftype == 'c')
701 (void) sprintf(fsize, "%3ld,%4ld",
702 major(p->fsize), minor(p->fsize));
703 else if (p->ftype == 's')
704 (void) sprintf(fsize, "%8d", 0);
705 else
706 (void) sprintf(fsize, p->fsize < 100000000 ? "%8lld" : \
707 "%lld", p->fsize);
708 /* get ftime */
709 {
710 char *cp = ctime(&p->fmtime);
711 if ((p->fmtime < sixmonthsago) || (p->fmtime > onehourfromnow))
712 (void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
713 else
714 (void) sprintf(ftime, " %-12.12s ", cp+4);
715 }
716 /* splat */
717 *lp++ = p->ftype;
718 lp = fmtmode(lp, p->fflags);
719 (void) sprintf(lp, "%c%3ld %s%s%s%s",
720 p->acl, p->fnl, uname, gflg ? gname : "", fsize, ftime);
721 return (lstuffbuf);
722 }
723
724 static int m1[] =
725 { 1, S_IREAD>>0, 'r', '-' };
726 static int m2[] =
727 { 1, S_IWRITE>>0, 'w', '-' };
728 static int m3[] =
729 { 3, S_ISUID|(S_IEXEC>>0), 's', S_IEXEC>>0, 'x', S_ISUID, 'S', '-' };
730 static int m4[] =
731 { 1, S_IREAD>>3, 'r', '-' };
732 static int m5[] =
733 { 1, S_IWRITE>>3, 'w', '-' };
734 static int m6[] =
735 { 3, S_ISGID|(S_IEXEC>>3), 's', S_IEXEC>>3, 'x', S_ISGID, 'S', '-' };
736 static int m7[] =
737 { 1, S_IREAD>>6, 'r', '-' };
738 static int m8[] =
739 { 1, S_IWRITE>>6, 'w', '-' };
740 static int m9[] =
741 { 3, S_ISVTX|(S_IEXEC>>6), 't', S_ISVTX, 'T', S_IEXEC>>6, 'x', '-' };
742
743 static int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
744
745 static char *
fmtmode(char * lp,int flags)746 fmtmode(char *lp, int flags)
747 {
748 int **mp;
749
750 for (mp = &m[0]; mp < &m[sizeof (m)/sizeof (m[0])]; ) {
751 register int *pairp = *mp++;
752 register int n = *pairp++;
753
754 while (n-- > 0) {
755 if ((flags&*pairp) == *pairp) {
756 pairp++;
757 break;
758 } else
759 pairp += 2;
760 }
761 *lp++ = *pairp;
762 }
763 return (lp);
764 }
765
766 /* rest should be done with nameserver or database */
767
768 #include <pwd.h>
769 #include <grp.h>
770 #include <utmpx.h>
771
772 #define NMAX (sizeof (((struct utmpx *)0)->ut_name))
773 #define SCPYN(a, b) strncpy(a, b, NMAX)
774
775
776 static struct cachenode { /* this struct must be zeroed before using */
777 struct cachenode *lesschild; /* subtree whose entries < val */
778 struct cachenode *grtrchild; /* subtree whose entries > val */
779 int val; /* the uid or gid of this entry */
780 int initted; /* name has been filled in */
781 char name[NMAX+1]; /* the string that val maps to */
782 } *names, *groups;
783
784 static struct cachenode *
findincache(struct cachenode ** head,id_t val)785 findincache(struct cachenode **head, id_t val)
786 {
787 register struct cachenode **parent = head;
788 register struct cachenode *c = *parent;
789
790 while (c != NULL) {
791 if (val == c->val) {
792 /* found it */
793 return (c);
794 } else if (val < c->val) {
795 parent = &c->lesschild;
796 c = c->lesschild;
797 } else {
798 parent = &c->grtrchild;
799 c = c->grtrchild;
800 }
801 }
802
803 /* not in the cache, make a new entry for it */
804 *parent = c = (struct cachenode *)calloc(1, sizeof (struct cachenode));
805 c->val = val;
806 return (c);
807 }
808
809 static char *
getname(uid_t uid)810 getname(uid_t uid)
811 {
812 struct cachenode *c;
813 struct passwd *pw;
814
815 c = findincache(&names, uid);
816 if (c->initted == 0) {
817 if ((pw = getpwuid(uid)) != NULL) {
818 (void) SCPYN(&c->name[0], pw->pw_name);
819 } else {
820 (void) sprintf(&c->name[0], "%-8lu ", uid);
821 }
822 c->initted = 1;
823 }
824 return (&c->name[0]);
825 }
826
827 static char *
getgroup(gid_t gid)828 getgroup(gid_t gid)
829 {
830 struct cachenode *c;
831 struct group *gr;
832
833 c = findincache(&groups, gid);
834 if (c->initted == 0) {
835 if ((gr = getgrgid(gid)) != NULL) {
836 (void) SCPYN(&c->name[0], gr->gr_name);
837 } else {
838 (void) sprintf(&c->name[0], "%-8lu ", gid);
839 }
840 c->initted = 1;
841 }
842 return (&c->name[0]);
843 }
844