#ifndef lint
static char sccsid[] = "@(#)getfacl.c 1.4 93/11/05 SMI";
#endif

/*
 * Copyright (c) 1993, by Sun Microsystems, Inc.
 */

/*
 * getfacl [-ad] file ...
 * This command displays discretionary information for a file or files.
 * display format:
 *	# file: filename
 *	# owner: uid
 *	# group: gid
 *	user::perm
 *	user:uid:perm
 *	group::perm
 *	group:gid:perm
 *	mask:perm
 *	other:perm
 *	default:user::perm
 *	default:user:uid:perm
 *	default:group::perm
 *	default:group:gid:perm
 *	default:mask:perm
 *	default:other:perm
 */

#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <locale.h>
#include <sys/acl.h>

static char	*pruname(uid_t);
static char	*prgname(gid_t);
static char	*display(int);
static void	usage();

main(int argc, char *argv[])
{
	int		c;
	int		aflag = 0;
	int		dflag = 0;
	int		errflag = 0;
	int		savecnt;
	int		aclcnt;
	int		mask;
	aclent_t	*aclp;
	aclent_t	*tp;
	char		*permp;

	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);

	if (argc < 2)
		usage();

	while ((c = getopt(argc, argv, "ad")) != EOF) {
		switch (c) {
		case 'a':
			aflag++;
			break;
		case 'd':
			dflag++;
			break;
		case '?':
			errflag++;
			break;
		}
	}
	if (errflag)
		usage();

	if (optind >= argc)
		usage();

	for (; optind < argc; optind++) {
		register char *filep;

		filep = argv[optind];

		/* Get ACL info of the files */
		if ((aclcnt = acl(filep, GETACLCNT, 0, NULL)) < 0) {
			(void) fprintf(stderr,
			    gettext("%s: failed to get acl count\n"), filep);
			exit(2);
		}
		if (aclcnt < MIN_ACL_ENTRIES) {
			(void) fprintf(stderr,
			    gettext("%d: acl count too small from %s\n"),
			    aclcnt, filep);
			exit(2);
		}

		if ((aclp = (aclent_t *)malloc(sizeof (aclent_t) * aclcnt))
		    == NULL) {
			(void) fprintf(stderr,
			    gettext("Insufficient memory\n"));
			exit(1);
		}
		if (acl(filep, GETACL, aclcnt, aclp) < 0) {
			(void) fprintf(stderr,
			    gettext("%s: failed to get acl entries\n"), filep);
			exit(2);
		}

		/* display ACL: assume it is sorted. */
		(void) printf("\n# file: %s\n", filep);
		savecnt = aclcnt;
		for (tp = aclp; aclcnt--; tp++) {
			if (tp->a_type == USER_OBJ)
				(void) printf("# owner: %s\n",
				    pruname(tp->a_id));
			if (tp->a_type == GROUP_OBJ)
				(void) printf("# group: %s\n",
				    prgname(tp->a_id));
			if (tp->a_type == CLASS_OBJ)
				mask = tp->a_perm;
		}
		aclcnt = savecnt;
		for (tp = aclp; aclcnt--; tp++) {
			switch (tp->a_type) {
			case USER:
				if (!dflag) {
					permp = display(tp->a_perm);
					(void) printf("user:%s:%s\t\t",
					    pruname(tp->a_id), permp);
					free(permp);
					permp = display(tp->a_perm & mask);
					(void) printf(
					    "#effective:%s\n", permp);
					free(permp);
				}
				break;
			case USER_OBJ:
				if (!dflag) {
					/* no need to display uid */
					permp = display(tp->a_perm);
					(void) printf("user::%s\n", permp);
					free(permp);
				}
				break;
			case GROUP:
				if (!dflag) {
					permp = display(tp->a_perm);
					(void) printf("group:%s:%s\t\t",
					    prgname(tp->a_id), permp);
					free(permp);
					permp = display(tp->a_perm & mask);
					(void) printf(
					    "#effective:%s\n", permp);
					free(permp);
				}
				break;
			case GROUP_OBJ:
				if (!dflag) {
					permp = display(tp->a_perm);
					(void) printf("group::%s\t\t", permp);
					free(permp);
					permp = display(tp->a_perm & mask);
					(void) printf(
					    "#effective:%s\n", permp);
					free(permp);
				}
				break;
			case CLASS_OBJ:
				if (!dflag) {
					permp = display(tp->a_perm);
					(void) printf("mask:%s\n", permp);
					free(permp);
				}
				break;
			case OTHER_OBJ:
				if (!dflag) {
					permp = display(tp->a_perm);
					(void) printf("other:%s\n", permp);
					free(permp);
				}
				break;
			case DEF_USER:
				if (!aflag) {
					permp = display(tp->a_perm);
					(void) printf("default:user:%s:%s\n",
					    pruname(tp->a_id), permp);
					free(permp);
				}
				break;
			case DEF_USER_OBJ:
				if (!aflag) {
					permp = display(tp->a_perm);
					(void) printf("default:user::%s\n",
					    permp);
					free(permp);
				}
				break;
			case DEF_GROUP:
				if (!aflag) {
					permp = display(tp->a_perm);
					(void) printf("default:group:%s:%s\n",
					    prgname(tp->a_id), permp);
					free(permp);
				}
				break;
			case DEF_GROUP_OBJ:
				if (!aflag) {
					permp = display(tp->a_perm);
					(void) printf("default:group::%s\n",
					    permp);
					free(permp);
				}
				break;
			case DEF_CLASS_OBJ:
				if (!aflag) {
					permp = display(tp->a_perm);
					(void) printf("default:mask:%s\n",
					    permp);
					free(permp);
				}
				break;
			case DEF_OTHER_OBJ:
				if (!aflag) {
					permp = display(tp->a_perm);
					(void) printf("default:other:%s\n",
					    permp);
					free(permp);
				}
				break;
			default:
				(void) fprintf(stderr,
				    gettext("unrecognized entry\n"));
				break;
			}
		}
		free(aclp);
	}
	exit(0);
}

static char *
display(int perm)
{
	char	*buf;

	buf = malloc(4);
	if (buf == NULL) {
		(void) fprintf(stderr, gettext("Insufficient memory\n"));
		exit(1);
	}

	if (perm & 4)
		buf[0] = 'r';
	else
		buf[0] = '-';
	if (perm & 2)
		buf[1] = 'w';
	else
		buf[1] = '-';
	if (perm & 1)
		buf[2] = 'x';
	else
		buf[2] = '-';
	buf[3] = '\0';
	return (buf);
}

static char *
pruname(uid_t uid)
{
	struct passwd	*passwdp;
	static char	uidp[10];	/* big enough */

	passwdp = getpwuid(uid);
	if (passwdp == (struct passwd *) NULL) {
		/* could not get passwd information: display uid instead */
		sprintf(uidp, "%d", uid);
		return (uidp);
	} else
		return (passwdp->pw_name);
}

static char *
prgname(gid_t gid)
{
	struct group	*groupp;
	static char	gidp[10];	/* big enough */

	groupp = getgrgid(gid);
	if (groupp == (struct group *) NULL) {
		/* could not get group information: display gid instead */
		sprintf(gidp, "%d", gid);
		return (gidp);
	} else
		return (groupp->gr_name);
}

static void
usage()
{
	(void) fprintf(stderr,
	    gettext("usage: getfacl [-ad] file ... \n"));
	exit(1);
}
