static uint 
parse_args (pam_handle_t *ph, int argc, const char **argv)
{
	uint args = 0;
	const void *svc;
	int only_if_len;
	int i;

	svc = NULL;
	if (pam_get_item (ph, PAM_SERVICE, &svc) != PAM_SUCCESS)
		svc = NULL;

	only_if_len = strlen ("only_if=");

	/* Parse the arguments */
	for (i = 0; i < argc; i++) {
		if (strcmp (argv[i], "auto_start") == 0) {
			args |= ARG_AUTO_START;

		} else if (strncmp (argv[i], "only_if=", only_if_len) == 0) {
			const char *value = argv[i] + only_if_len;
			if (!evaluate_inlist (svc, value))
				args |= ARG_IGNORE_SERVICE;

		} else if (strcmp (argv[i], "use_authtok") == 0) {
			args |= ARG_USE_AUTHTOK;

		} else {
			syslog (GKR_LOG_WARN, "gkr-pam: invalid option: %s",
				argv[i]);
		}
	}
	
	return args;
}
/* Match a triple. */
static int
evaluate(pam_handle_t *pamh, int debug,
	 const char *left, const char *qual, const char *right,
	 struct passwd *pwd, const char *user)
{
	char buf[LINE_MAX] = "";
	const char *attribute = left;
	/* Figure out what we're evaluating here, and convert it to a string.*/
	if ((strcasecmp(left, "login") == 0) ||
	    (strcasecmp(left, "name") == 0) ||
	    (strcasecmp(left, "user") == 0)) {
		snprintf(buf, sizeof(buf), "%s", user);
		left = buf;
	}
	if (strcasecmp(left, "uid") == 0) {
		snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_uid);
		left = buf;
	}
	if (strcasecmp(left, "gid") == 0) {
		snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_gid);
		left = buf;
	}
	if (strcasecmp(left, "shell") == 0) {
		snprintf(buf, sizeof(buf), "%s", pwd->pw_shell);
		left = buf;
	}
	if ((strcasecmp(left, "home") == 0) ||
	    (strcasecmp(left, "dir") == 0) ||
	    (strcasecmp(left, "homedir") == 0)) {
		snprintf(buf, sizeof(buf), "%s", pwd->pw_dir);
		left = buf;
	}
	if (strcasecmp(left, "service") == 0) {
		const void *svc;
		if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS)
			svc = "";
		snprintf(buf, sizeof(buf), "%s", (const char *)svc);
		left = buf;
	}
	/* If we have no idea what's going on, return an error. */
	if (left != buf) {
		pam_syslog(pamh, LOG_CRIT, "unknown attribute \"%s\"", left);
		return PAM_SERVICE_ERR;
	}
	if (debug) {
		pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
			   attribute, left);
	}

	/* Attribute value < some threshold. */
	if ((strcasecmp(qual, "<") == 0) ||
	    (strcasecmp(qual, "lt") == 0)) {
		return evaluate_lt(pamh, left, right);
	}
	/* Attribute value <= some threshold. */
	if ((strcasecmp(qual, "<=") == 0) ||
	    (strcasecmp(qual, "le") == 0)) {
		return evaluate_le(pamh, left, right);
	}
	/* Attribute value > some threshold. */
	if ((strcasecmp(qual, ">") == 0) ||
	    (strcasecmp(qual, "gt") == 0)) {
		return evaluate_gt(pamh, left, right);
	}
	/* Attribute value >= some threshold. */
	if ((strcasecmp(qual, ">=") == 0) ||
	    (strcasecmp(qual, "ge") == 0)) {
		return evaluate_ge(pamh, left, right);
	}
	/* Attribute value == some threshold. */
	if (strcasecmp(qual, "eq") == 0) {
		return evaluate_eqn(pamh, left, right);
	}
	/* Attribute value = some string. */
	if (strcasecmp(qual, "=") == 0) {
		return evaluate_eqs(left, right);
	}
	/* Attribute value != some threshold. */
	if (strcasecmp(qual, "ne") == 0) {
		return evaluate_nen(pamh, left, right);
	}
	/* Attribute value != some string. */
	if (strcasecmp(qual, "!=") == 0) {
		return evaluate_nes(left, right);
	}
	/* Attribute value matches some pattern. */
	if ((strcasecmp(qual, "=~") == 0) ||
	    (strcasecmp(qual, "glob") == 0)) {
		return evaluate_glob(left, right);
	}
	if ((strcasecmp(qual, "!~") == 0) ||
	    (strcasecmp(qual, "noglob") == 0)) {
		return evaluate_noglob(left, right);
	}
	/* Attribute value matches item in list. */
	if (strcasecmp(qual, "in") == 0) {
		return evaluate_inlist(left, right);
	}
	if (strcasecmp(qual, "notin") == 0) {
		return evaluate_notinlist(left, right);
	}
	/* User is in this group. */
	if (strcasecmp(qual, "ingroup") == 0) {
		return evaluate_ingroup(pamh, user, right);
	}
	/* User is not in this group. */
	if (strcasecmp(qual, "notingroup") == 0) {
		return evaluate_notingroup(pamh, user, right);
	}
	/* (Rhost, user) is in this netgroup. */
	if (strcasecmp(qual, "innetgr") == 0) {
		const void *rhost;
		if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
			rhost = NULL;
		return evaluate_innetgr(rhost, user, right);
	}
	/* (Rhost, user) is not in this group. */
	if (strcasecmp(qual, "notinnetgr") == 0) {
		const void *rhost;
		if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
			rhost = NULL;
		return evaluate_notinnetgr(rhost, user, right);
	}
	/* Fail closed. */
	return PAM_SERVICE_ERR;
}
/* Check for list mismatch. */
static int
evaluate_notinlist(const char *left, const char *right)
{
	return evaluate_inlist(left, right) != PAM_SUCCESS ? PAM_SUCCESS : PAM_AUTH_ERR;
}