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