/***************************************************************************/
/***************************************************************************/
/* */
/* (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