#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 *