#include "ftptool.h"

#pragma ident   "@(#)readdir.c 1.12     93/12/16"

extern int dirlastmtime;

#ifdef USE_PROTOTYPES
struct dirlist *read_local_dir(char *dirname)
#else
struct dirlist *read_local_dir(dirname)
char	*dirname;
#endif
{
	struct dirlist *head;
	DIR	*dir;
	struct dirent *dp;
	struct passwd *psswd;
	struct group *grp;
	struct stat buf;
	time_t	curtime;
	time_t	time();
	char	date[DATELEN];
	struct tm *tm;
	char	owner[20];
	char	group[20];


	head = new_dirlist("", "", "", "", 0, (size_t)0);
	if (head == NULL) {
		fprintf(stderr, "Out of memory\n");
		goto out;
	}

	if (lstat(dirname, &buf) == -1) {
		local_footer_message("Could not stat directory %s.", dirname);
		goto out;
	}

	dirlastmtime = buf.st_mtime;

	if ((dir = opendir(dirname)) == NULL) {
		local_footer_message("Could not open directory %s.", dirname);
		goto out;
	}

	curtime = time((time_t *)NULL);


	while ((dp = readdir(dir)) != NULL) {
		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
			continue;
		bzero((char *)&buf, sizeof (buf));
		if (lstat(dp->d_name, &buf) == -1) {
			buf.st_mode = 0;
			buf.st_uid = 0;
			buf.st_gid = 0;
			buf.st_size = 0;
			buf.st_mtime = 0;
		}
		tm = localtime(&buf.st_mtime);
		if ((curtime - buf.st_mtime) < (6 * 30 * 24 * 60 * 60))
			strftime(date, sizeof (date), "%h %e %H:%M", tm);
		else
			strftime(date, sizeof (date), "%h %e  %Y", tm);

		/* determine user and group ids */
		psswd = getpwuid(buf.st_uid);
		if (psswd == NULL) {
			sprintf(owner, "%d", (int) buf.st_uid);
		} else
			strcpy(owner, psswd->pw_name);

		grp = getgrgid(buf.st_gid);
		if (grp == NULL) {
			sprintf(group, "%d", (int) buf.st_gid);
		} else
			strcpy(group, grp->gr_name);

		/* if a link, format name to 'name -> link' as ftp would */
		if (S_ISLNK(buf.st_mode)) {
			char	*linkvalue;
			int		nbytes;

			linkvalue = (char *)malloc(MAXPATHLEN + 1);
			if ((nbytes = readlink(dp->d_name, linkvalue,
			    (int)buf.st_size)) == -1)
				strcpy(linkvalue, "unknown");
			else
				linkvalue[nbytes] = '\0';
			sprintf(scratch, "%s -> %s", dp->d_name, linkvalue);
			free(linkvalue);
		} else
			strcpy(scratch, dp->d_name);
		if (add_dirname(head, scratch, date, owner, group,
		    buf.st_mode, (size_t)buf.st_size, local_sort_mode,
		    local_sort_direction) == NULL)
			goto out;
	}
	closedir(dir);

out:
	if (head)
		return (head);
	return (NULL);
}

#ifdef USE_PROTOTYPES
struct dirlist *read_remote_dir(void)
#else
struct dirlist *read_remote_dir()
#endif
{
	struct dirlist *head = NULL;
	int	old_unix, temp_non_unix = 0;
	FILE	*din = NULL;
	int		vms_expect_dir;

restart:
	old_unix = temp_non_unix;
	head = new_dirlist("", "", "", "", 0, (size_t)0);
	if (head == NULL) {
		fprintf(stderr, "Out of memory\n");
		goto out;
	}
	/* read the remote directory, adding files and directories to a list */
	/* send dir command */
	if (non_unix || temp_non_unix) {
		din = open_remote_ls(1);
		vms_expect_dir = 0;
	} else {
		din = open_remote_ls(0);
		vms_expect_dir = 1;
	}
	if (din == NULL) {
		/* always have .. */
		return (head);
	}

	for (;;) {
		if (next_remote_line(din) == NULL)
			goto out;
		if (!strncmp(response_line, "226", 3) ||
		    !strncmp(response_line, "250", 3)) {
			/* done */
			log_message(response_line);
			break;
		}
		/* ignore blank lines */
		if (*response_line == '\n' || *response_line == '\0')
			continue;
		/* VMS also prints the directory name */
		if (remote_os_type == REMOTE_OS_VMS && vms_expect_dir) {
			/* first non-blank line is directory name */
			vms_expect_dir = 0;
			continue;
		}
		/* VMS goofy total line before 226 */
		if (!strncmp(response_line, "Total of", 8))
			continue;
		if (parse_line(head, response_line, &temp_non_unix)) {
			if (old_unix != temp_non_unix) {
				/* start over */
				free_dirlist(head);
				head = NULL;
				while (getc(din) != -1)
					/* NULL */;
				close_remote_ls(din);
				din = NULL;
				goto restart;
			} else
				goto out;
		}
	}

out:
	if (din)
		close_remote_ls(din);
	if (timedout) {
		if (head)
			free_dirlist(head);
		timeout_disconnect();
		return (NULL);
	}
	if (head)
		return (head);
	return (NULL);
}


#define	NULLCHECK() if (*curr == '\0') { *temp_non_unix = 1; return (1); }

#ifdef USE_PROTOTYPES
int	parse_line(struct dirlist *head, char *line, int *temp_non_unix)
#else
int	parse_line(head, line, temp_non_unix)
struct dirlist *head;
char	*line;
int		*temp_non_unix;
#endif
{
	if (non_unix || *temp_non_unix) {
		return (parse_line_ls(head, line));
	}
	switch (remote_os_type) {
	case REMOTE_OS_UNIX:
		return (parse_line_pattern(head, unix_dir_pattern, line,
		    temp_non_unix));
		break;
	case REMOTE_OS_VMS:
		return (parse_line_vms(head, line, temp_non_unix));
		break;
	case REMOTE_OS_DOS:
		return (parse_line_dos(head, line, temp_non_unix));
		break;
	case REMOTE_OS_OTHER:
		return (parse_line_pattern(head, other_dir_pattern, line,
		    temp_non_unix));
		break;
	}
	return (1);
}

#ifdef USE_PROTOTYPES
int	parse_line_ls(struct dirlist *head, char *line)
#else
int	parse_line_ls(head, line)
struct dirlist *head;
char	*line;
#endif
{
	char	*curr;
	static char	name[MAXPATHLEN + 1];
	char	*tmp;
	mode_t	type;

	/* Assume that if the first character is upper case, */
	/* it is non-unix (since ls never puts upper case as the type */
	/* And it appears that the filename is all the characters at the */
	/* start of the line up to but not including white space on VMS and */
	/* tops20. This would make it functional, partially at least */
	/* Also, may need to turn on temp_non_unix_mode, so that cd is */
	/* possible on anything, since we can't tell if they're directories. */
	/* or not */
	curr = line;

	while (*curr && isspace(*curr))
		curr++;
	tmp = name;
	while (*curr && (*curr != '\n')) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	curr++;

	type = S_IFLNK;
	if (name[strlen(name)] == '/')
		type = S_IFDIR;

	if (add_dirname(head, name, "unknown", "unknown", "unknown", type,
		(size_t)-1, remote_sort_mode, remote_sort_direction) == NULL) {
		fprintf(stderr, "add_dirname failed!\n");
		return (1);
	}
	return (0);
}

#ifdef USE_PROTOTYPES
int	parse_line_pattern(struct dirlist *head, char *pattern,
	char *line, int *temp_non_unix)
#else
int	parse_line_pattern(head, pattern, line, temp_non_unix)
struct dirlist *head;
char	*pattern;
char	*line;
int		*temp_non_unix;
#endif
{
	/* default mode is a symbolic link. This is so if you don't have */
	/* UNIX PERMS, it can be either a file or a directory. Maybe.  */
	mode_t	mode = S_IFLNK;
	int		intmode;
	char	*curr;
	static char	date[20];
	static char	month[10];
	static char	day[10];
	static char	timeyear[10];
	static char	name[MAXPATHLEN + 1];
	static char owner[25];
	static char group[25];
	size_t	size = (size_t)-1;
	char	*tmp;
	char	*dirtmp;

	month[0] = '\0';
	day[0] = '\0';
	timeyear[0] = '\0';
	date[0] = '\0';
	name[0] = '\0';
	strcpy(owner, "unknown");
	strcpy(group, "unknown");

	curr = line;
	dirtmp = pattern;
	while (*dirtmp != '\0') {
		NULLCHECK();
		switch (*dirtmp) {
		case ' ':
			while (*curr && isspace(*curr))
				curr++;
			break;
		case SKIP:
			while (*curr && !isspace(*curr))
				curr++;
			break;
		case PERMS:
			intmode = unix_perms(curr, temp_non_unix);
			if (intmode == 0)
				return (0);
			else if (intmode == -1)
				return (1);
			mode = (mode_t)intmode;
			while (*curr && !isspace(*curr))
				curr++;
			break;
		case LINKS:
			/* dump link count */
			while (isdigit(*curr))
				curr++;
			break;
		case USER:
			/*
			 * this should be the user name, surrounded by
			 * white space
			 */
			tmp = owner;
			while (*curr && !isspace(*curr)) {
				*tmp = *curr;
				tmp++;
				curr++;
			}
			*tmp = '\0';
			break;
		case GROUP:
			/*
			 * this should be the group name, surrounded by
			 * white space
			 */
			tmp = group;
			while (*curr && !isspace(*curr)) {
				*tmp = *curr;
				tmp++;
				curr++;
			}
			*tmp = '\0';
			break;
		case SIZE:
			/* first test only true for UNIX case, where we have */
			/* seen the perms */
			if (S_ISCHR(mode) || S_ISBLK(mode)) {
				/* size is actually major, minor */
				while (isdigit(*curr) || *curr == ',')
					curr++;
				NULLCHECK();
				while (*curr && isspace(*curr))
					curr++;
				NULLCHECK();
				while (isdigit(*curr))
					curr++;
				size = -1;
			} else {
				sscanf(curr, "%d", &size);
				while (isdigit(*curr))
					curr++;
			}
			break;
		case MONTH:
			tmp = month;
			while (isalpha(*curr)) {
				*tmp = *curr;
				tmp++;
				curr++;
			}
			*tmp = '\0';
			break;
		case DAY:
			tmp = day;
			while (isdigit(*curr)) {
				*tmp = *curr;
				tmp++;
				curr++;
			}
			*tmp = '\0';
			break;
		case TIME:
			tmp = timeyear;
			while (isdigit(*curr) || (*curr == ':')) {
				*tmp = *curr;
				tmp++;
				curr++;
			}
			*tmp = '\0';
			break;
		case NAME:
		case LOWERNAME:
			tmp = name;
			/*
			 * The following test makes sure we have seen the
			 * PERMS field.  If not, the permissions will
			 * still be 0. symlinks normally have 777 for
			 * permissions
			 */
			if (mode != S_IFLNK && S_ISLNK(mode)) {
				while (*curr && *curr != '\n') {
					*tmp = *curr;
					tmp++;
					curr++;
				}
			} else {
				/* if token last in line, eat till newline */
				if (*(dirtmp + 1) == '\0') {
					while (*curr && *curr != '\n') {
						*tmp = *curr;
						tmp++;
						curr++;
					}
				} else {
					while (*curr && !isspace(*curr) &&
					    *curr != ';') {
						*tmp = *curr;
						tmp++;
						curr++;
					}
				}
				/* VMS */
				if (*curr == ';') {
					while (*curr && !isspace(*curr))
						curr++;
				}
			}
			*tmp = '\0';
			if (*dirtmp == LOWERNAME) {
				for (tmp = name; *tmp != '\0'; tmp++)
					if (isupper(*tmp))
						*tmp = tolower(*tmp);
			}
			break;
		default:
			if (*dirtmp == *curr) {
				curr++;
			} else {
				*temp_non_unix = 1;
				return (1);
			}
		}
		dirtmp++;
	}
	sprintf(date, "%s %2s %5s", month, day, timeyear);

	/* The wuarchive ftp server prints . and .. in the listing */
	/* ignore them */
	if ((name[0] == '.' && name[1] == '\0') ||
	    (name[0] == '.' && name[1] == '.' && name[2] == '\0')) {
		return (0);
	}

	if (add_dirname(head, name, date, owner, group, mode, size,
		remote_sort_mode, remote_sort_direction) == NULL) {
		fprintf(stderr, "add_dirname failed!\n");
		return (1);
	}
	return (0);
}

static char *abbrev_month[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};



#ifdef USE_PROTOTYPES
int	parse_line_vms(struct dirlist *head, char *line, int *temp_non_unix)
#else
int	parse_line_vms(head, line, temp_non_unix)
struct dirlist *head;
char	*line;
int		*temp_non_unix;
#endif
{
	/* default mode is a symbolic link. This is so if you don't have */
	/* UNIX PERMS, it can be either a file or a directory. Maybe.  */
	mode_t	mode = S_IFLNK;
	int		intmode;
	char	*curr;
	static char	date[20];
	static char	month[10];
	static char	day[10];
	static char	hour[10];
	static char	year[10];
	static char	name[MAXPATHLEN + 1];
	static char owner[25];
	static char group[25];
	static int partial = 0;
	size_t	size = (size_t)-1;
	char	*tmp;
	time_t	curtime, filetime;
	extern int cummonthdays[];
	int	i;
	int	founddash;
	int day_i, month_i, year_i, min_i, hour_i;

	month[0] = '\0';
	day[0] = '\0';
	hour[0] = '\0';
	year[0] = '\0';
	date[0] = '\0';
	if (partial == 0)
		name[0] = '\0';

	strcpy(owner, "unknown");
	strcpy(group, "unknown");

	curr = line;

	/* Format is */
	/*
	 *
	 * NETINFO_ROOT:[000000]
	 *
	 * 0README.;25            7   1-DEC-1991 16:55 NETINFO (RWD,RWD,,R)
	 * STATUS.19-MAR-1992;1
	 *                        7  19-MAR-1992 12:48 NETINFO (RWED,RWED,RE,RE)
	 * UOFA.DIR;1             2  15-SEP-1989 11:49 NETINFO (RWE,RWE,RE,RE)
	 *
	 */
	/*
	 * Blank lines are already ignored.
	 * Directory name should already be ignored too.
	 */
	if (partial == 0) {
		tmp = name;
		while (*curr && !isspace(*curr)) {
			*tmp = *curr;
			tmp++;
			curr++;
		}
		*tmp = '\0';
		/*
		 * Got the name. However, it may break lines into two, as
		 * STATUS above. After reading a name, see if we have to
		 * save and wait for the next line.
		 */
		tmp = curr;
		while (*tmp && isspace(*tmp))
			tmp++;
		if (*tmp == '\0') {
			partial = 1;
			return (0);
		}
	} else {
		/* already have name from previous call */
		partial = 0;
	}

	while (isspace(*curr))
		curr++;
	NULLCHECK();

	/* size, in 512 byte blocks? */
	/* could be missing. */
	tmp = curr;
	founddash = 0;
	while (!isspace(*tmp)) {
		if (*tmp == '-') {
			founddash = 1;
			break;
		}
		tmp++;
	}
	if (!founddash) {
		sscanf(curr, "%d", &size);
		while (isdigit(*curr))
			curr++;
		size *= 512;
	}

	while (isspace(*curr))
		curr++;

	/* Date: 1-DEC-1991 16:55 */
	tmp = day;
	while (isdigit(*curr)) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	if (*curr != '-')
		return (1);
	curr++;
	NULLCHECK();

	tmp = month;
	while (isalpha(*curr)) {
		if (tmp != month && isupper(*curr))
			*tmp = tolower(*curr);
		else
			*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	if (*curr != '-')
		return (1);
	curr++;
	NULLCHECK();

	tmp = year;
	while (isdigit(*curr)) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	if (*curr != ' ')
		return (1);
	curr++;
	NULLCHECK();

	tmp = hour;
	while (isdigit(*curr) || *curr == ':') {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	while (*curr && !isspace(*curr)) {
		curr++;
	}
	NULLCHECK();

	curr++;
	NULLCHECK();

	/*
	 * this should be the user name, surrounded by white space
	 */
	tmp = owner;
	while (!isspace(*curr)) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	curr++;
	NULLCHECK();

	while (isspace(*curr))
		curr++;

	intmode = vms_perms(curr, temp_non_unix);
	if (intmode == 0)
		return (0);
	else if (intmode == -1)
		return (1);
	mode = (mode_t)(intmode | vms_filetype(name));

	day_i = atoi(day);
	month_i = 0;
	for (i = 0; i < 12; i++)
		if (!strncmp(month, abbrev_month[i], 3))
			break;
	if (i != 12)
		month_i = i;
	year_i = atoi(year);
	year_i -= 1970; /* time kept since 1970 */

	hour_i = min_i = 0;
	sscanf(hour, "%d:%d", &hour_i, &min_i);


	filetime = ((year_i * 365) + cummonthdays[month_i] + day_i) * 24 * 60;
	filetime += min_i + (hour_i * 60);
	curtime = time((time_t *)NULL);
	curtime /= 60; /* seconds -> minutes */


	if ((curtime - filetime) < (6 * 30 * 24 * 60))
		sprintf(date, "%s %2s %5s", month, day, hour);
	else
		sprintf(date, "%s %2s %5s", month, day, year);

	/* change name to lowercase, and remove ';num' */
	tmp = strrchr(name, ';');
	if (tmp != NULL)
		*tmp = '\0';
	for (tmp = name; *tmp != '\0'; tmp++)
		if (isupper(*tmp))
			*tmp = tolower(*tmp);
	if (add_dirname(head, name, date, owner, group, mode, size,
		remote_sort_mode, remote_sort_direction) == NULL) {
		fprintf(stderr, "add_dirname failed!\n");
		return (1);
	}
	return (0);
}

#ifdef USE_PROTOTYPES
int	parse_line_dos(struct dirlist *head, char *line, int *temp_non_unix)
#else
int	parse_line_dos(head, line, temp_non_unix)
struct dirlist *head;
char	*line;
int		*temp_non_unix;
#endif
{
	/* default mode is a symbolic link. This is so if you don't have */
	/* UNIX PERMS, it can be either a file or a directory. Maybe.  */
	mode_t	mode = S_IFLNK;
	char	*curr;
	static char	date[20];
	static char	month[10];
	static char	day[10];
	static char	year[10];
	static char	hour[10];
	static char	name[MAXPATHLEN + 1];
	size_t	size = (size_t)-1;
	char	*tmp;
	time_t	curtime, filetime;
	int	i;
	int	day_i, month_i, year_i, min_i, hour_i;
	extern int cummonthdays[];

	month[0] = '\0';
	day[0] = '\0';
	year[0] = '\0';
	hour[0] = '\0';
	date[0] = '\0';
	name[0] = '\0';

	/* Format is */
	/*
    	 * 	33430            IO.SYS   Tue Apr 09 05:00:00 1991
    	 * 	37394         MSDOS.SYS   Tue Apr 09 05:00:00 1991
    	 * 	47845       COMMAND.COM   Tue Apr 09 05:00:00 1991
	 * <dir>                    DOS   Wed Sep 23 19:43:32 1992
	 * <dir>                  WPWIN   Fri Oct 30 03:04:50 1992
     	 * 	9349         WINA20.386   Tue Apr 09 05:00:00 1991
	 * <dir>                VWCOMMS   Tue Dec 01 02:52:44 1992
     	 * 	123          CONFIG.SYS   Fri Nov 27 03:17:40 1992
     	 * 	589        AUTOEXEC.BAT   Tue Dec 01 02:42:42 1992
	 */
	curr = line;
	while (isspace(*curr))
		curr++;
	NULLCHECK();

	/* directory or file+size? */
	if (isdigit(*curr)) {
		/* file size */
		mode = S_IFREG;
		sscanf(curr, "%d", &size);
		while (isdigit(*curr))
			curr++;
	} else if (!strncasecmp(curr, "<dir>", 5)) {
		/* directory */
		mode = S_IFDIR;
		while (!isspace(*curr))
			curr++;
	} else {
		*temp_non_unix = 1;
		return (1);
	}
	while (isspace(*curr))
		curr++;
	NULLCHECK();

	/* name */
	tmp = name;
	while (!isspace(*curr)) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	while (isspace(*curr))
		curr++;
	NULLCHECK();

	/* Skip day of week */
	while (!isspace(*curr))
		curr++;
	while (isspace(*curr))
		curr++;
	NULLCHECK();

	/* month */
	tmp = month;
	while (isalpha(*curr)) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	while (isspace(*curr))
		curr++;
	NULLCHECK();

	/* day */
	tmp = day;
	while (isdigit(*curr)) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	while (isspace(*curr))
		curr++;
	NULLCHECK();


	/* hour:minute:seconds */
	tmp = hour;
	while (isdigit(*curr) || *curr == ':') {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';
	while (!isspace(*curr))
		curr++;
	NULLCHECK();

	/* year */
	tmp = year;
	while (isdigit(*curr)) {
		*tmp = *curr;
		tmp++;
		curr++;
	}
	*tmp = '\0';

	day_i = atoi(day);
	month_i = 0;
	for (i = 0; i < 12; i++)
		if (!strncmp(month, abbrev_month[i], 3))
			break;
	if (i != 12)
		month_i = i;
	year_i = atoi(year);
	year_i -= 1970; /* time kept since 1970 */

	hour_i = min_i = 0;
	sscanf(hour, "%d:%d", &hour_i, &min_i);


	filetime = ((year_i * 365) + cummonthdays[month_i] + day_i) * 24 * 60;
	filetime += min_i + (hour_i * 60);
	curtime = time((time_t *)NULL);
	curtime /= 60; /* seconds -> minutes */


	if ((curtime - filetime) < (6 * 30 * 24 * 60))
		sprintf(date, "%s %2s %5s", month, day, hour);
	else
		sprintf(date, "%s %2s %5s", month, day, year);

	/* change name to lowercase */
	for (tmp = name; *tmp != '\0'; tmp++)
		if (isupper(*tmp))
			*tmp = tolower(*tmp);

	if (add_dirname(head, name, date, "unknown", "unknown", mode, size,
		remote_sort_mode, remote_sort_direction) == NULL) {
		fprintf(stderr, "add_dirname failed!\n");
		return (1);
	}
	return (0);
}

/*
 * take the dir_parse string, in the form of
 *	PERMS LINKS USER GROUP SIZE MONTH DAY TIME NAME
 * and return a NULL-terminated array of character values of the above
 *
 * The values would be control-characters, so they are not in danger
 * of being typed (right!). A ' ' represents whitespace, any other character
 * must be matched exactly.
 */

char	*lex_string;

#ifdef USE_PROTOTYPES
char *dir_parse_to_pattern(char *dir_parse)
#else
char *dir_parse_to_pattern(dir_parse)
char	*dir_parse;
#endif
{
	char 	pattern[MAXPATHLEN+1];
	char	*nextpos = pattern;
	char	*s;
	int		token;
	int		found_bitmask = 0;
	int		yylex();
	static char *tokval[] = {
		"",
		"PERMS",
		"LINKS",
		"USER",
		"GROUP",
		"SIZE",
		"MONTH",
		"DAY",
		"TIME",
		"NAME",
		"SKIP",
		"NONUNIX",
		"LOWERNAME",
	};

	lex_string = strdup(dir_parse);
	if (lex_string == NULL) {
		fprintf(stderr, "Out of memory\n");
		exit(1);
	}
	s = lex_string;
	while ((token = yylex()) != 0) {
		*nextpos = (char)token;
		nextpos++;
		if (token <= MAXTOKENS) {
			if (found_bitmask & (1 << token)) {
				fprintf(stderr,
				    "Duplicate token %s in DIR template.\n",
				    tokval[token]);
				free(s);
				return (NULL);
			}
			if (token != SKIP)
				found_bitmask |= 1 << token;
		}
	}
	*nextpos = '\0';
	free(s);
	if ((found_bitmask & (1 << NONUNIX)) != 0) {
		pattern[0] = (char)NONUNIX;
		return (strdup(pattern));
	}
	/* make sure that we have a filename, at least */
	if ((found_bitmask & (1 << NAME)) == 0 &&
	    (found_bitmask & (1 << LOWERNAME)) == 0) {
		fprintf(stderr,
		    "You must specify a NAME token in the parse field.\n");
		return (NULL);
	}
	return (strdup(pattern));
}

#undef NULLCHECK
#define	NULLCHECK() if (*s == '\0') { *temp_non_unix = 1; return (-1); }

#ifdef USE_PROTOTYPES
int unix_perms(char *s, int *temp_non_unix)
#else
int unix_perms(s, temp_non_unix)
char	*s;
int		*temp_non_unix;
#endif
{
	int mode;

	switch (*s) {
	case 'd':
			mode = S_IFDIR;
			break;
	case 'F':
	case 'f':
	case 'm': /* Cray and convex migrated files */
	case '-':
			mode = S_IFREG;
			break;
	case 'l':
			mode = S_IFLNK;
			break;
	case 'b':
			mode = S_IFBLK;
			break;
	case 'c':
			mode = S_IFCHR;
			break;
	case 's':
			mode = S_IFSOCK;
			break;
	case 'p':
			mode = S_IFIFO;
			break;
	case 'D':
			mode = S_IFDIR;
			break;
	case 'B':
			mode = S_IFBLK;
			break;
	case 'C':
			mode = S_IFCHR;
			break;
	case 'S':
			mode = S_IFSOCK;
			break;
	case 'P':
			mode = S_IFIFO;
			break;
	case 't':
			if (!strncmp(s, "total", 5))
				return (0);
			/* fall through */
#ifdef notdef
	case '.':
		if (s[1] == ':') {
			ftperr = ftp_error(' ', "Permission denied");
			footer_message(ftperr);
			return (0);
		} else if (!strncmp(s, ". unreadable", 12)) {
			footer_message(". unreadable.");
			return (0);
		}
#endif
	default:
			*temp_non_unix = 1;
			return (-1);
			break;
	}

	s++;
	if (*s == ' ' || *s == '\0' || *s == '\t')
		return (mode); /* OK to not have permissions */
	NULLCHECK();
	/*
	 * Determine permissions.
	 */

#define	SET_PERM1(ch, val) \
	{ \
		if (*s == ch) {\
			mode |= val; \
		} else if (*s != '-') { \
			*temp_non_unix = 1; \
			return (-1); \
		} \
		s++; \
	}

#define	SET_PERM2(ch1, ch2, val) \
	{ \
		if (*s == ch1 || *s == ch2) {\
			mode |= val; \
		} else if (*s != '-') { \
			*temp_non_unix = 1; \
			return (-1); \
		} \
		s++; \
	}


	SET_PERM1('r', S_IRUSR);
	NULLCHECK();

	SET_PERM1('w', S_IWUSR);
	NULLCHECK();

	if (*s == 'S')
		*s = '-';
	SET_PERM2('x', 's', S_IXUSR);
	NULLCHECK();

	SET_PERM1('r', S_IRGRP);
	NULLCHECK();

	SET_PERM1('w', S_IWGRP);
	NULLCHECK();

	if (*s == 'S')
		*s = '-';
	SET_PERM2('x', 's', S_IXGRP);
	NULLCHECK();

	SET_PERM1('r', S_IROTH);
	NULLCHECK();

	SET_PERM1('w', S_IWOTH);
	NULLCHECK();

	if (*s == 'T')
		*s = '-';
	SET_PERM2('x', 't', S_IXOTH);
	NULLCHECK();

	return (mode);
}

#ifdef USE_PROTOTYPES
int vms_perms_set(char *s)
#else
int vms_perms_set(s)
char	*s;
#endif
{
	int	set = 0;
	char	*tmp;

	for (tmp = s; *tmp != '\0'; tmp++) {
		/* handle RWED */
		switch (*tmp) {
		case 'R':
			set |= S_IROTH;
			break;
		case 'W':
			set |= S_IWOTH;
			break;
		case 'E':
			set |= S_IXOTH;
			break;
		case 'D':
			/* ignore */
			break;
		default:
			return (set);
		}
	}
	return (set);
}

#ifdef USE_PROTOTYPES
int vms_perms(char *s, int *temp_non_unix)
#else
int vms_perms(s, temp_non_unix)
char	*s;
int		*temp_non_unix;
#endif
{
	int mode;

	/* s is of the form: */
	/* (RWED,RWED,RWED,RWED) */
	/* system, owner, group, world */

	if (*s != '(')
		return (-1);
	s++;
	NULLCHECK();
	/*
	 * Skip system.
	 */
	while (*s && *s != ',')
		s++;
	NULLCHECK();
	/* otherwise, it's a comma */
	s++;

	/* owner */
	mode = vms_perms_set(s) << 6;

	while (*s && *s != ',')
		s++;
	NULLCHECK();
	/* otherwise, it's a comma */
	s++;

	/* group */
	mode |= vms_perms_set(s) << 3;

	while (*s && *s != ',')
		s++;
	NULLCHECK();
	/* otherwise, it's a comma */
	s++;

	/* world */
	mode |= vms_perms_set(s);

	return (mode);
}

#ifdef USE_PROTOTYPES
int vms_filetype(char *s)
#else
int vms_filetype(s)
char	*s;
#endif
{
	char *period;

	period = strrchr(s, '.');
	if (period != NULL) {
		if (!strncmp(period, ".DIR", 4)) {
			/* Zap .DIR* for */
			*period = '\0';
			return (S_IFDIR);
		}
	}
	return (S_IFREG);
}


syntax highlighted by Code2HTML, v. 0.9.1