13 #include <sys/select.h>
22 #define select select_s
26 int update_localdir(void);
28 int keypress(int key);
29 static void fixname(char *dest, const char *src);
30 static void xfer_done(struct ftp *ftp, struct ftp_transfer *xfer);
31 int parse_args(int argc, char **argv);
33 static struct ftp *ftp;
34 static struct tui_widget *uilist[2];
38 static char *host = "localhost";
41 static char curdir[PATH_MAX + 1];
42 static struct ftp_dirent *localdir;
43 static int local_modified;
46 int main(int argc, char **argv)
48 int i, numsock, maxfd;
53 if(parse_args(argc, argv) == -1) {
57 localdir = darr_alloc(0, sizeof *localdir);
58 getcwd(curdir, sizeof curdir);
61 if(!(ftp = ftp_alloc())) {
64 if(ftp_connect(ftp, host, port) == -1) {
76 uilist[0] = tui_list("Remote", 0, 1, 40, 21, 0, 0);
77 uilist[1] = tui_list("Local", 40, 1, 40, 21, 0, 0);
79 tui_focus(uilist[focus], 1);
82 tg_bgcolor(TGFX_BLACK);
83 tg_rect("No conn.", 0, 0, 80, 1, 0);
94 numsock = ftp_sockets(ftp, ftpsock, sizeof ftpsock);
95 for(i=0; i<numsock; i++) {
96 FD_SET(ftpsock[i], &rdset);
97 if(ftpsock[i] > maxfd) maxfd = ftpsock[i];
105 tv.tv_sec = tv.tv_usec = 0;
108 if(select(maxfd + 1, &rdset, 0, 0, &tv) == -1 && errno == EINTR) {
113 if(FD_ISSET(0, &rdset)) {
114 if(proc_input() == -1) {
119 if(proc_input() == -1) {
124 for(i=0; i<numsock; i++) {
125 if(FD_ISSET(ftpsock[i], &rdset)) {
126 ftp_handle(ftp, ftpsock[i]);
143 struct ftp_dirent *ent;
144 unsigned int upd = 0;
148 if(ftp->status != FTP_DISC) {
149 tg_fgcolor(TGFX_GREEN);
150 tg_bgcolor(TGFX_BLACK);
151 tg_text(0, 0, "Conn: %s", host);
154 remdir = ftp_curdir(ftp);
155 if(remdir && strcmp(tui_get_title(uilist[0]), remdir) != 0) {
156 tui_set_title(uilist[0], remdir);
160 if(ftp->modified & FTP_MOD_DIR) {
161 tui_clear_list(uilist[0]);
163 num = ftp_num_dirent(ftp);
164 for(i=0; i<num; i++) {
165 ent = ftp_dirent(ftp, i);
166 if(ent->type == FTP_DIR) {
167 sprintf(buf, "%s/", ent->name);
168 tui_add_list_item(uilist[0], buf);
170 tui_add_list_item(uilist[0], ent->name);
174 tui_list_select(uilist[0], 0);
176 ftp->modified &= ~FTP_MOD_DIR;
180 if(local_modified || strcmp(tui_get_title(uilist[1]), curdir) != 0) {
181 tui_clear_list(uilist[1]);
182 num = darr_size(localdir);
183 for(i=0; i<num; i++) {
185 if(ent->type == FTP_DIR) {
186 sprintf(buf, "%s/", ent->name);
187 tui_add_list_item(uilist[1], buf);
189 tui_add_list_item(uilist[1], ent->name);
192 tui_list_select(uilist[1], 0);
193 tui_set_title(uilist[1], curdir);
199 if(tui_isdirty(uilist[0]) || upd & 1) {
202 if(tui_isdirty(uilist[1]) || upd & 2) {
207 int update_localdir(void)
212 struct ftp_dirent ent;
214 if(!(dir = opendir(curdir))) {
215 errmsg("failed to open directory: %s\n", curdir);
219 darr_clear(localdir);
220 while((dent = readdir(dir))) {
221 ent.name = strdup_nf(dent->d_name);
222 if(strcmp(dent->d_name, ".") == 0) continue;
224 if(stat(dent->d_name, &st) == 0) {
225 if(S_ISDIR(st.st_mode)) {
230 ent.size = st.st_size;
236 darr_push(localdir, &ent);
240 qsort(localdir, darr_size(localdir), sizeof *localdir, ftp_direntcmp);
249 while(poll_input(&ev)) {
252 if(keypress(ev.key.key) == -1) {
264 int keypress(int key)
275 tui_focus(uilist[focus], 0);
277 tui_focus(uilist[focus], 1);
281 tui_list_sel_prev(uilist[focus]);
284 tui_list_sel_next(uilist[focus]);
287 tui_list_sel_start(uilist[focus]);
290 tui_list_sel_end(uilist[focus]);
294 sel = tui_get_list_sel(uilist[focus]);
296 struct ftp_dirent *ent = ftp_dirent(ftp, sel);
297 if(ent->type == FTP_DIR) {
298 ftp_queue(ftp, FTP_CHDIR, ent->name);
303 if(localdir[sel].type == FTP_DIR) {
304 if(chdir(localdir[sel].name) == -1) {
305 errmsg("failed to change directory: %s\n", localdir[sel].name);
307 getcwd(curdir, sizeof curdir);
318 ftp_queue(ftp, FTP_CDUP, 0);
320 if(chdir("..") == 0) {
321 getcwd(curdir, sizeof curdir);
328 sel = tui_get_list_sel(uilist[focus]);
330 struct ftp_transfer *xfer;
331 struct ftp_dirent *ent = ftp_dirent(ftp, sel);
332 char *lname = alloca(strlen(ent->name) + 1);
334 fixname(lname, ent->name);
336 xfer = malloc_nf(sizeof *xfer);
337 if(!(xfer->fp = fopen(lname, "wb"))) {
338 errmsg("failed to open %s: %s\n", lname, strerror(errno));
344 xfer->rname = strdup_nf(ent->name);
345 xfer->total = ent->size;
346 xfer->done = xfer_done;
348 ftp_queue_transfer(ftp, xfer);
361 static void fixname(char *dest, const char *src)
368 if((suffix = strrchr(dest, '.'))) {
370 if(strlen(suffix) > 3) {
374 if(strlen(dest) > 8) {
380 strcat(dest, suffix);
386 static void xfer_done(struct ftp *ftp, struct ftp_transfer *xfer)
396 static const char *usage = "Usage: %s [options] [hostname] [port]\n"
398 " -h: print usage information and exit\n";
400 int parse_args(int argc, char **argv)
404 for(i=1; i<argc; i++) {
405 if(argv[i][0] == '-') {
406 if(argv[i][2] != 0) {
411 printf(usage, argv[0]);
425 if((port = atoi(argv[i])) <= 0) {
426 fprintf(stderr, "invalid port number: %s\n", argv[i]);
439 fprintf(stderr, "invalid argument: %s\n", argv[i]);
440 fprintf(stderr, usage, argv[0]);