/*
 access.c
*/


#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<errno.h>


int access(const char *pathname, int mode)
{
	struct stat st;
	uid_t uid;
	gid_t gid;
	mode_t fileMode;

	if ((mode & (F_OK | R_OK | W_OK | X_OK)) == 0){
		errno = EINVAL;
		return -1;
	}
	if (stat(pathname, &st) < 0){
		return -1;
	}
	if (mode == F_OK){
		return 0;
	}
	
	uid = geteuid();
	if ((uid == 0) && (st.st_mode & S_IFDIR)){
		return 0;
	}
	gid = getegid();
	if (mode & W_OK){
		if (st.st_uid == uid){
			fileMode = S_IWUSR | S_IWGRP | S_IWOTH;
		}
		else if (st.st_gid == gid){
			fileMode = S_IWGRP | S_IWOTH;
		}
		else{
			fileMode = S_IWOTH;
		}
		if ((st.st_mode & fileMode) == 0){
			errno = EACCES;
			return -1;
		}
	}
	if (mode & R_OK){
		if (st.st_uid == uid){
			fileMode = S_IRUSR | S_IRGRP | S_IROTH;
		}
		else if (st.st_gid == gid){
			fileMode = S_IRGRP | S_IROTH;
		}
		else{
			fileMode = S_IROTH;
		}
		if ((st.st_mode & fileMode) == 0){
			errno = EACCES;
			return -1;
		}
	}
	if (mode & X_OK){
		if (st.st_uid == uid){
			fileMode = S_IXUSR | S_IXGRP | S_IXOTH;
		}
		else if (st.st_gid == gid){
			fileMode = S_IXGRP | S_IXOTH;
		}
		else{
			fileMode = S_IXOTH;
		}
		if ((st.st_mode & fileMode) == 0){
			errno = EACCES;
			return -1;
		}
	}

	return 0;
}
