/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (c) 1995-1999.  The Regents of the University of California.  All     */
/*   rights reserved.                                                      */
/*                                                                         */
/*   This work was produced at the University of California, Lawrence      */
/*   Livermore National Laboratory (UC LLNL) under contract no.            */
/*   W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy     */
/*   (DOE) and The Regents of the University of California (University)    */
/*   for the operation of UC LLNL.  Copyright is reserved to the           */
/*   University for purposes of controlled dissemination,                  */
/*   commercialization through formal licensing, or other disposition      */
/*   under terms of Contract 48; DOE policies, regulations and orders;     */
/*   and U.S. statutes.  The rights of the Federal Government are          */
/*   reserved under Contract 48 subject to the restrictions agreed upon    */
/*   by the DOE and University.                                            */
/*                                                                         */
/*                                                                         */
/*                              DISCLAIMER                                 */
/*                                                                         */
/*   This software was prepared as an account of work sponsored by an      */
/*   agency of the United States Government.  Neither the United States    */
/*   Government nor the University of California nor any of their          */
/*   employees, makes any warranty, express or implied, or assumes any     */
/*   liability or responsibility for the accuracy, completeness, or        */
/*   usefulness of any information, apparatus, product, or process         */
/*   disclosed, or represents that its specific commercial products,       */
/*   process, or service by trade name, trademark, manufacturer, or        */
/*   otherwise, does not necessarily constitute or imply its               */
/*   endorsement, recommendation, or favoring by the United States         */
/*   Government or the University of California. The views and opinions    */
/*   of the authors expressed herein do not necessarily state or reflect   */
/*   those of the United States Government or the University of            */
/*   California, and shall not be used for advertising or product          */
/*   endorsement purposes.                                                 */
/*                                                                         */
/*   Permission to use, copy, modify and distribute this software and its  */
/*   documentation for any non-commercial purpose, without fee, is         */
/*   hereby granted, provided that the above copyright notice and this     */
/*   permission notice appear in all copies of the software and            */
/*   supporting documentation, and that all UC LLNL identification in      */
/*   the user interface remain unchanged.  The title to copyright LLNL     */
/*   XDIR shall at all times remain with The Regents of the University     */
/*   of California and users agree to preserve same. Users seeking the     */
/*   right to make derivative works with LLNL XDIR for commercial          */
/*   purposes may obtain a license from the Lawrence Livermore National    */
/*   Laboratory's Technology Transfer Office, P.O. Box 808, L-795,         */
/*   Livermore, CA 94550.                                                  */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef apollo
#include <sys/dir.h>
#else
#ifdef NEXT
#include <bsd/sys/dir.h>
#else
#include <dirent.h>
#endif
#endif
#include <errno.h>
#include <ctype.h>
#include <Xm/Xm.h>
#include "xdir.h"
#include "list.h"
#include "str.h"

#ifdef apollo
#define dirent direct
#endif

#ifdef __EMX__
#define lstat stat
#endif

#ifndef S_ISLNK
#define S_ISLNK(mode)   (((mode)&0170000) == 0120000) /* Kludge for nonPOSIX */
#endif

#ifndef S_ISSOCK
#define S_ISSOCK(mode)  (((mode)&0170000) == 0140000) /* Kludge for nonPOSIX */
#endif

#define MAXDIRLINE  200

extern int max_ftp_retries;
extern int diagnostics;
extern struct st_host_info hinfo[];
extern int maxt;
extern int passive_mode;

struct sl_struct *create_array_list();
char *ifgets();
struct sl_struct *create_null_array_list();
int cmp_long_vms_entries();

 
/* 
 * local_ls - Sets "dlist" to a pointer to a structure that contains a
 *            sorted list of directory entries in "directory" on the
 *            local host.  Set "sym_mode" to True if type symbol is to
 *            be appended to directory entries.  Set "dotfiles" to True
 *            if dot files are to be included.  Returns 0 if successful,
 *            else returns -1.  If unable to read the directory because
 *            of lack of permission, returns 0 and a single entry named
 *            ".unreadable".
 */ 
local_ls(directory, dlist, sym_mode, dotfiles)
char *directory;
struct sl_struct **dlist;
int sym_mode;
int dotfiles;
{ 
	struct entry_link *head = NULL;
	DIR *dirp;
	struct dirent *dp;
	char entry[MAXPATHLEN];
	struct stat status;
	int retval;

	if ((dirp = opendir(directory)) == NULL) {
		if (errno == EACCES) {
			add_to_linked_list(&head, ".unreadable");
			goto done;
		} else
			return -1;
	}

	if (sym_mode)
		retval = chdir(directory);

	for (dp=readdir(dirp); dp!=NULL; dp=readdir(dirp))
		if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..") 
				&& (dotfiles || dp->d_name[0] != '.')) {
			if (sym_mode && !retval && !lstat(dp->d_name, &status)) {
				strcpy(entry, dp->d_name);
				if (S_ISREG(status.st_mode)) {
					if ((S_IXUSR | S_IXGRP | S_IXOTH) & status.st_mode)
						strcat(entry, "*");
				} else if (S_ISDIR(status.st_mode))
					strcat(entry, "/");
				else if (S_ISLNK(status.st_mode))
					strcat(entry, "@");
				else if (S_ISSOCK(status.st_mode))
					strcat(entry, "=");
				else if ((S_IXUSR | S_IXGRP | S_IXOTH) & status.st_mode)
					strcat(entry, "*");
				add_to_linked_list(&head, entry);
			} else
	       		add_to_linked_list(&head, dp->d_name);
		}
	
	closedir(dirp);

done:
 
    /* Convert linked list into array */
    *dlist = create_array_list(&head);
 
    /* Sort entries */
	if (sym_mode)
		sort_symbol_entries((*dlist)->entries, (*dlist)->nentries);
	else
    	quicksort((*dlist)->entries, (*dlist)->nentries, strcmp);

    return 0;
}


/* 
 * local_dir - Sets "dlist" to a pointer to a structure that contains a
 *             sorted list of directory entries in "directory" on the
 *             local host.  Set "dotfiles" to True if dot files are to
 *             be included.  Returns 0 if successful, else returns -1.
 */ 
local_dir(directory, dlist, dotfiles)    
char *directory;
struct sl_struct **dlist;
int dotfiles;
{
    struct entry_link *head = NULL;
    char dirline[MAXDIRLINE+1];
    FILE *fp;
    int len;
    int dummy;
	char cmd[MAXPATHLEN+15];
	char *name;
	char *info;
	char type;

	save_sigcld_handler();
#ifdef __EMX__
	sprintf(cmd, "ls -gal %s", directory);
#else
	sprintf(cmd, "/bin/ls -gal %s", directory);
#endif
    if ((fp = popen(cmd, "r")) == NULL) {
		restore_sigcld_handler();
		return -1;
	}

    while (ifgets(dirline, MAXDIRLINE, fp) != NULL) {
        len = strlen(dirline);
        if (dirline[len-1] == '\n')
            dirline[--len] = '\0';
		if (sscanf(dirline, "total %d", &dummy) == 1)
			continue;
		unix_parse_long_entry(dirline, &name, &info, &type);
		if (strcmp(name, ".") && strcmp(name, "..") &&
				(dotfiles || name[0] != '.'))
			add_to_linked_list(&head, dirline);
		XtFree(name);
		XtFree(info);
    }
	restore_sigcld_handler();

    if (ferror(fp)) {
		report_perror("Getting long list");
		release_linked_list(&head);
		return -1;
	}

    pclose(fp);

    /* Convert linked list into array */
    *dlist = create_array_list(&head);

	return 0;
}


/*
 * remote_dirlist - Set "dlist" to a pointer to a structure that
 *                  contains a list of entries in "directory" on the
 *                  remote host.  Set "cmd" to either "NLST" or
 *                  "LIST", optionally followed by an option
 *                  (e.g., "NLST -lagt").  "host" is the host id.
 *                  Returns 0 if successful, -3 for broken connection,
 *                  -6 for stop button pressed, and -1 for other errors.
 */
remote_dirlist(host, directory, cmd, dlist)
int host;
char *directory;
char *cmd;
struct sl_struct **dlist;
{
    char ftpline[MAXFTPLINE+1];
    struct entry_link *head;
    int data_fd;
    char ch;
    int nbytes;
    int code;
    char reply[MAXFTPREPLY];
    int sock;
    int linelen;
    char tcpbuf[MAXTCPBUF];
    int len_tcpbuf;
    int index_tcpbuf;
    int retval;
	int retries = 0;
	int scode;
	char temp[20];
	char *request;
	int stop_was_pressed = False;
	int errval;

    /* Verify that request is valid */
	strcpy(temp, cmd);
	if ((request = strtok(temp, " ")) == NULL)
		return -1;
    if (strcmp(request, "NLST") && strcmp(request, "LIST"))
        return -1;

	/* Always use ASCII mode for lists */
	if ((retval = ftp_type(host, ASCII)) < 0)
		return retval;

retry:

	/* Initialize linked list of directory entries */
	head = NULL;

    /* "cd" to directory */
    if ((retval = remote_cd(host, directory, True)) < 0)
        return retval;

    /* Initialize data connection */
	if (passive_mode) {
		if ((data_fd = passive_data_conn(host)) < 0)
			return data_fd;
	} else if ((sock = init_data_conn(host)) < 0)
		return sock;

    /* Send command to FTP server */
    if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		errval = -1;
		goto error;
	} else if (retval < 0) {
		errval = retval;
		goto error;
    }

    /* Get initial response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
    switch (scode) {
    case 1:
        break;
    case 4:
		if (passive_mode)
			close(data_fd);
		else
			close(sock);
        if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
            if (diagnostics >= VERBOSE)
				report_retry("Directory list");
            goto retry;
        }
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
        return -1;
	case 5:
		if (passive_mode)
			close(data_fd);
		else
			close(sock);
		if (code == 550)     /* Kludge to handle empty UNICOS directories */
			goto convert;
		else if (code == 501)
			goto convert;    /* Kludge to handle empty MacOS directories */
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	case -6:
		abort_ftp_cmd(host);
    case -3:
		errval = scode;
		goto error;
	case -2:
		report_client_timeout(host);
    default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		errval = -1;
		goto error;
    }

    /* Establish data connection for nonpassive mode*/
	if (!passive_mode) {
	    data_fd = accept_data_conn(sock);
		close(sock);
	    if (data_fd < 0)
			return -1;
	}

    /* Get directory list from FTP server */
    len_tcpbuf = 0;
    index_tcpbuf = 0;
    linelen = 0;
    while (1) {
        if (index_tcpbuf >= len_tcpbuf) {
			nbytes = read_tcp(host, data_fd, tcpbuf, MAXTCPBUF, maxt);
			switch (nbytes) {
			case -6:
				stop_was_pressed = True;
			case -2:
				release_linked_list(&head);
				goto abort;
			case -1:
			case -3:
				release_linked_list(&head);
				close(data_fd);
				return nbytes;
			case 0:
				goto got_list;
			}
            index_tcpbuf = 0;
            len_tcpbuf = nbytes;
        }
        ch = tcpbuf[index_tcpbuf++];
        if (ch == '\n') {
            ftpline[linelen] = '\0';
            if (diagnostics == DEBUG) {
                write_log(ftpline);
                write_log("\n");
            }
            add_to_linked_list(&head, ftpline);
            linelen = 0;
        } else if (ch != '\r') {
            if (linelen >= MAXFTPLINE) {
                release_linked_list(&head);
                close(data_fd);
                return -1;
            }
            ftpline[linelen++] = ch;
        }
    }

got_list:

    /* Get final response from FTP server */
	close(data_fd);
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
    switch (scode) {
    case 2:
        break;
	case -6:
		abort_ftp_cmd(host);
    case -3:
		release_linked_list(&head);
        return scode;
	case -2:
		report_client_timeout(host);
    default:
		if (scode == 4 && code/10 == 45 && retries++ < max_ftp_retries) {
            if (diagnostics >= VERBOSE)
				report_retry("Directory list");
			release_linked_list(&head);
            goto retry;
        }
		if (code == 550) {    /* Kludge to handle empty VMS directories */
			break;
		}
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		release_linked_list(&head);
        return -1;
    }

convert:

    /* Convert linked list into array */
    *dlist = create_array_list(&head);

    return 0;

abort:

    /* Send urgent abort to server */
	if (urgent_abort(host) == -6)
		stop_was_pressed = True;

    /* Clear out all incoming data */
    while ((retval = read_tcp(host, data_fd, tcpbuf, MAXTCPBUF, 10)) > 0);
	if (retval == -6)
		stop_was_pressed = True;
    close(data_fd);

    /* Get responses from server */
    retval = get_ftp_reply(host, reply, MAXFTPREPLY, &code, 15);
	if (retval == -6)
		stop_was_pressed = True;
	if (retval > 0) {
        retval = get_ftp_reply(host, reply, MAXFTPREPLY, &code, 5);
		if (retval == -6)
			stop_was_pressed = True;
	}

	/* Report any timeout */
	if (retval == -2)
		report_client_timeout(host);

	/* Want to check for lost host before issuing next command */
	hinfo[host].last_reply_time = 0;

	/* Determine return value */
	if (stop_was_pressed)
		return -6;
	else if ((retval == -3) || (retval == -2))
		return -3;
	else
		return -1;

error:

	if (passive_mode)
		close(data_fd);
	else
		close(sock);
	return errval;
}


/*
 * remote_ls - Sets "dlist" to a pointer to a structure that contains
 *             a sorted list of directory entries in "directory" on the
 *             remote host.  "host" is the host id.  Set sym_mode to
 *             True if type symbol is to be appended to directory
 *             entries.  Set "dotfiles" to True if dot files are to
 *             be included.  Returns 0 if successful, -3 for broken
 *             connection, -6 for stop button pressed, and -1 for
 *             other errors.
 */
remote_ls(host, directory, dlist, sym_mode, dotfiles)
int host;
char *directory;
struct sl_struct **dlist;
int sym_mode;
int dotfiles;
{
    int retval;
	int i;
	int j;
	int len;
	char *dirline;

    /* Get short list */
	if (sym_mode && ((hinfo[host].system == SYS_UNIX)
			|| (hinfo[host].server == SERVER_MAC_FTPD)
			|| (hinfo[host].system == SYS_NT)))
		retval = remote_dirlist(host, directory, "NLST -aF", dlist);
	else if (dotfiles && (hinfo[host].system == SYS_UNIX))
		retval = remote_dirlist(host, directory, "NLST -a", dlist);
	else
		retval = remote_dirlist(host, directory, "NLST", dlist);

    if (retval < 0)
        return retval;

	/* Kludge for certain VMS FTP servers */
	if (hinfo[host].server == SERVER_VMS_3_0)
		for (i=0; i<(*dlist)->nentries; i++) {
			len = strlen((*dlist)->entries[i]);
			for (j=0; j<len; j++)
				(*dlist)->entries[i][j] = tolower((*dlist)->entries[i][j]);
		}

	/* Remove trailing slashes from certain FTP servers */
	if (!sym_mode && (hinfo[host].server == SERVER_MAC_VERSATERM 
			|| hinfo[host].server == SERVER_MAC_NCSATELNET
			|| hinfo[host].server == SERVER_DOS_WINQVT))
		for (i=0; i<(*dlist)->nentries; i++) {
			len = strlen((*dlist)->entries[i]);
			if (len > 0 && (*dlist)->entries[i][len-1] == '/')
				(*dlist)->entries[i][len-1] = '\0';
		}

	/* Delete unwanted entries */
	if (hinfo[host].system != SYS_VMS)
		for (i=(*dlist)->nentries-1; i>=0; i--) {
			dirline = (*dlist)->entries[i];
			if (sym_mode) {
				if (!strcmp(dirline, "./") || !strcmp(dirline, "../"))
					delete_from_array_list(dlist, i);
			} else {
				if (!strcmp(dirline, ".") || !strcmp(dirline, ".."))
					delete_from_array_list(dlist, i);
			}
		}

	/* Process dot files */
	if (!dotfiles)
		for (i=(*dlist)->nentries-1; i>=0; i--) {
			if ((*dlist)->entries[i][0] == '.')
				delete_from_array_list(dlist, i);
		}

    /* Sort entries */
	if (sym_mode && ((hinfo[host].system == SYS_UNIX)
			|| (hinfo[host].system == SYS_NT)
			|| (hinfo[host].server == SERVER_MAC_FTPD)
			|| (hinfo[host].server == SERVER_MAC_VERSATERM)
			|| (hinfo[host].server == SERVER_MAC_NCSATELNET)
			|| (hinfo[host].server == SERVER_DOS_WINQVT)))
		sort_symbol_entries((*dlist)->entries, (*dlist)->nentries);
	else
    	quicksort((*dlist)->entries, (*dlist)->nentries, strcmp);

    return 0;
}


/*
 * remote_dir - Sets "dlist" to a pointer to a structure that contains 
 *              a long list of directory entries in "directory" on the
 *              remote host.  "host" is the host id.  Set "dotfiles"
 *              to True if dot files are to be included.  Returns 0 if
 *              successful, -3 for broken connection, -6 for stop button
 *              pressed, and -1 for other errors.
 */
remote_dir(host, directory, dlist, dotfiles)
int host;
char *directory;
struct sl_struct **dlist;
int dotfiles;
{
	int i;
	int j;
	int retval;
	int dummy;
	int len;
	char *dirline;
	char *name;
	char *info;
	char type;
	char ch;
	int count;
	char *prev_dirline;
	char *temp;

    /* Get extended list */
	if (dotfiles && (hinfo[host].system == SYS_UNIX) &&
			(hinfo[host].server != SERVER_UNIX_UNITREE) &&
			(hinfo[host].server != SERVER_UNIX_NSL_UNITREE))
		retval = remote_dirlist(host, directory, "NLST -gal", dlist);
	else
    	retval = remote_dirlist(host, directory, "LIST", dlist);
	if (retval)
		return retval;

	/* Done if no entries */
	if ((*dlist)->nentries == 0)
		return 0;

	/* Delete unwanted lines */
	switch (hinfo[host].system) {
	case SYS_UNIX:
	case SYS_WINDOWS:
		for (i=(*dlist)->nentries-1; i>=0; i--) {
			dirline = (*dlist)->entries[i];
			len = strlen(dirline);
			if (sscanf(dirline, "total %d", &dummy) == 1)
				delete_from_array_list(dlist, i);
		}
		break;
	case SYS_VMS:
		if ((*dlist)->nentries < 4)
			goto error;
		dirline = (*dlist)->entries[(*dlist)->nentries-1];
		if ((count = extracted_vms_file_count(dirline)) < 0)
			goto error;
		delete_from_array_list(dlist, (*dlist)->nentries-1);
		for (i=2; i>=0; i--)
			delete_from_array_list(dlist, i);
		for (i=(*dlist)->nentries-1; i>=0; i--)
			if (strlen((*dlist)->entries[i]))
				break;
			else
				delete_from_array_list(dlist, i);
		if ((*dlist)->nentries < count)
			goto error;
		for (i=(*dlist)->nentries-1; i>=0; i--) {
			dirline = (*dlist)->entries[i];
			if (dirline[0] == ' ') {
				if (i == 0)
					goto error;
				len = strlen(dirline);
				for (j=0; j<len; j++)
					if (dirline[j] != ' ')
						break;
				prev_dirline = (*dlist)->entries[i-1];
				temp = XtMalloc(strlen(prev_dirline)+strlen(&(dirline[j]))+3);
				strcpy(temp, prev_dirline);
				strcat(temp, "  ");
				strcat(temp, &(dirline[j]));
				delete_from_array_list(dlist, i);
				i--;
				XtFree((*dlist)->entries[i]);
				(*dlist)->entries[i] = temp;
			}
		}
		if (count != (*dlist)->nentries)
			goto error;
	}

	/* Get rid of unwanted entries */
	for (i=(*dlist)->nentries-1; i>=0; i--) {
		parse_long_entry(hinfo[host].system, hinfo[host].server,
			(*dlist)->entries[i], &name, &info, &type);
		if (((hinfo[host].system == SYS_UNIX ||
				hinfo[host].system == SYS_WINDOWS) &&
				(!strcmp(name, ".") || !strcmp(name, ".."))) ||
				(!dotfiles && name[0] == '.'))
			delete_from_array_list(dlist, i);
		XtFree(name);
		XtFree(info);
	}

	/* Kludge for certain VMS servers that sort funny */
	if (hinfo[host].server == SERVER_VMS_3_0)
		quicksort((*dlist)->entries, (*dlist)->nentries, cmp_long_vms_entries);

	return 0;

error:
	
	release_array_list(*dlist);
	return -1;
}


/*
 * extracted_vms_file_count - Extracts the number of files specified in the
 *                            last line "line"  of a long list of a nonempty
 *                            VMS directory.  This routine looks for a
 *                            pattern of the form " xxx files." at the end
 *                            of "line".  If this pattern is found, the
 *                            file length is returned, otherwise -1 is
 *                            returned.
 */
extracted_vms_file_count(line)
char *line;
{
	int count;
	int i;
	char *ptr;

	/* Number appears before string " file" */
	ptr = strstr(line, " file");

	for (i=ptr-line-1; i>=0; i--)
		if (!isdigit(line[i]))
			break;
	if ((i < 0) || (i == ptr-line-1) || (line[i] != ' '))
		return -1;
	ptr = &(line[i+1]);
	count = 0;
	while (isdigit(*ptr))
		count = (count*10)+(*ptr++-'0');
	return count;
}


/*
 * get_dirlist - Sets "dlist" to a pointer to a structure that contains
 *               a list of directory entries in directory "path" for host
 *               "host".  "layout" determines the format of the directory
 *               list.  Set "dotfiles" to True if dot files are to be
 *               included in the directory list.  Set "cfetch_ok" to
 *               True if it is ok to fetch the directory list from the
 *               cache.  Set "cstore_ok" to True if it is ok to use the
 *               directory list to update the cache.  Returns 0 if
 *               successful, 1 if list came from cache, -3 for broken
 *               connection, -6 for stop button pressed, and -1 for other
 *               errors.
 */
get_dirlist(host, path, layout, dotfiles, cstore_ok, cfetch_ok, dlist)
int host;
char *path;
int layout;
int dotfiles;
int cstore_ok;
int cfetch_ok;
struct sl_struct **dlist;
{
	int retval;
	int i;
	char *name;
	char *info;
	char type;

	/* Can the directory list cache be used? */
	if (cfetch_ok && (host != LOCAL)) {
		if (fetch_dirlist_from_cache(host, path, layout, dotfiles, dlist))
			return 1;
	}

	/* Get directory list */
	if (host == LOCAL)
		switch (layout) {
		case TABULAR:
			retval = local_ls(path, dlist, False, dotfiles);
			break;
		case ICONIC:
		case TREE:
			retval = local_ls(path, dlist, True, dotfiles);
			break;
		case FULL_INFO:
			retval = local_dir(path, dlist, dotfiles);
		}
	else {
		switch (layout) {
		case TABULAR:
			retval = remote_ls(host, path, dlist, False, dotfiles);
			break;
		case ICONIC:
		case TREE:
			if (hinfo[host].server == SERVER_UNIX_NSL_UNITREE ||
					hinfo[host].server == SERVER_WINDOWS_WFTPD) {
				if (!(retval = remote_dir(host, path, dlist, dotfiles)))
					for (i=0; i<(*dlist)->nentries; i++) {
						unix_parse_long_entry((*dlist)->entries[i], &name, &info,
							&type);
						XtFree((*dlist)->entries[i]);
						XtFree(info);
						(*dlist)->entries[i] = XtMalloc(strlen(name)+2);
						strcpy((*dlist)->entries[i], name);
						XtFree(name);
						switch (type) {
						case DIRECTORY_TYPE:
							strcat((*dlist)->entries[i], "/");
							break;
						case LINK_TYPE:
							strcat((*dlist)->entries[i], "@");
							break;
						case SOCKET_TYPE:
							strcat((*dlist)->entries[i], "=");
							break;
						case EXECUTABLE_TYPE:
							strcat((*dlist)->entries[i], "*");
						}
					}
			} else
				retval = remote_ls(host, path, dlist, True, dotfiles);
			break;
		case FULL_INFO:
			retval = remote_dir(host, path, dlist, dotfiles);
		}
		if (!retval && cstore_ok)
			add_dirlist_to_cache(host, path, layout, dotfiles, *dlist);
	}

	return retval;
}



syntax highlighted by Code2HTML, v. 0.9.1