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;
44 static struct ftp_transfer *cur_xfer;
47 void detect_lfn(void);
53 int main(int argc, char **argv)
55 int i, numsock, maxfd;
64 if(parse_args(argc, argv) == -1) {
68 localdir = darr_alloc(0, sizeof *localdir);
69 getcwd(curdir, sizeof curdir);
72 if(!(ftp = ftp_alloc())) {
75 if(ftp_connect(ftp, host, port) == -1) {
87 uilist[0] = tui_list("Remote", 0, 1, 40, 21, 0, 0);
88 uilist[1] = tui_list("Local", 40, 1, 40, 21, 0, 0);
90 tui_focus(uilist[focus], 1);
100 numsock = ftp_sockets(ftp, ftpsock, sizeof ftpsock);
101 for(i=0; i<numsock; i++) {
102 FD_SET(ftpsock[i], &rdset);
103 if(ftpsock[i] > maxfd) maxfd = ftpsock[i];
111 tv.tv_sec = tv.tv_usec = 0;
114 if(select(maxfd + 1, &rdset, 0, 0, &tv) == -1 && errno == EINTR) {
119 if(FD_ISSET(0, &rdset)) {
120 if(proc_input() == -1) {
125 if(proc_input() == -1) {
130 for(i=0; i<numsock; i++) {
131 if(FD_ISSET(ftpsock[i], &rdset)) {
132 ftp_handle(ftp, ftpsock[i]);
149 struct ftp_dirent *ent;
150 unsigned int upd = 0;
153 static int prev_status;
154 static void *prev_xfer;
156 if(ftp->status != prev_status) {
157 tg_fgcolor(ftp->status ? TGFX_GREEN : TGFX_RED);
158 tg_bgcolor(TGFX_BLACK);
159 tg_text(0, 0, "Srv: %s", ftp->status ? host : "-");
161 prev_status = ftp->status;
164 tg_fgcolor(TGFX_WHITE);
165 tg_bgcolor(TGFX_BLACK);
167 int dir = tg_gchar(cur_xfer->op == FTP_RETR ? TGFX_RARROW : TGFX_LARROW);
168 tg_text(40, 0, "%c%s", dir, cur_xfer->rname);
169 if(!cur_xfer->total) {
170 tg_text(75, 0, " ???%%");
172 progr = 100 * cur_xfer->count / cur_xfer->total;
173 if(progr < 0) progr = 0;
174 if(progr > 100) progr = 100;
175 tg_text(75, 0, " %3d%%", progr);
178 } else if(prev_xfer) {
179 tg_rect(0, 40, 0, 40, 1, 0);
182 prev_xfer = cur_xfer;
184 remdir = ftp_curdir(ftp);
185 if(remdir && strcmp(tui_get_title(uilist[0]), remdir) != 0) {
186 tui_set_title(uilist[0], remdir);
190 if(ftp->modified & FTP_MOD_DIR) {
191 tui_clear_list(uilist[0]);
193 num = ftp_num_dirent(ftp);
194 for(i=0; i<num; i++) {
195 ent = ftp_dirent(ftp, i);
196 if(ent->type == FTP_DIR) {
197 sprintf(buf, "%s/", ent->name);
198 tui_add_list_item(uilist[0], buf);
200 tui_add_list_item(uilist[0], ent->name);
204 tui_list_select(uilist[0], 0);
206 ftp->modified &= ~FTP_MOD_DIR;
210 if(local_modified || strcmp(tui_get_title(uilist[1]), curdir) != 0) {
211 tui_clear_list(uilist[1]);
212 num = darr_size(localdir);
213 for(i=0; i<num; i++) {
215 if(ent->type == FTP_DIR) {
216 sprintf(buf, "%s/", ent->name);
217 tui_add_list_item(uilist[1], buf);
219 tui_add_list_item(uilist[1], ent->name);
222 tui_list_select(uilist[1], 0);
223 tui_set_title(uilist[1], curdir);
229 if(tui_isdirty(uilist[0]) || upd & 1) {
232 if(tui_isdirty(uilist[1]) || upd & 2) {
241 int update_localdir(void)
246 struct ftp_dirent ent;
248 if(!(dir = opendir(curdir))) {
249 errmsg("failed to open directory: %s\n", curdir);
253 darr_clear(localdir);
254 while((dent = readdir(dir))) {
255 ent.name = strdup_nf(dent->d_name);
256 if(strcmp(dent->d_name, ".") == 0) continue;
258 if(stat(dent->d_name, &st) == 0) {
259 if(S_ISDIR(st.st_mode)) {
264 ent.size = st.st_size;
270 darr_push(localdir, &ent);
274 qsort(localdir, darr_size(localdir), sizeof *localdir, ftp_direntcmp);
283 while(poll_input(&ev)) {
286 if(keypress(ev.key.key) == -1) {
298 int keypress(int key)
309 tui_invalidate(uilist[0]);
310 tui_invalidate(uilist[1]);
314 tui_focus(uilist[focus], 0);
316 tui_focus(uilist[focus], 1);
320 tui_list_sel_prev(uilist[focus]);
323 tui_list_sel_next(uilist[focus]);
326 tui_list_sel_start(uilist[focus]);
329 tui_list_sel_end(uilist[focus]);
333 sel = tui_get_list_sel(uilist[focus]);
335 struct ftp_dirent *ent = ftp_dirent(ftp, sel);
336 if(ent->type == FTP_DIR) {
337 ftp_queue(ftp, FTP_CHDIR, ent->name);
342 if(localdir[sel].type == FTP_DIR) {
343 if(chdir(localdir[sel].name) == -1) {
344 errmsg("failed to change directory: %s\n", localdir[sel].name);
346 getcwd(curdir, sizeof curdir);
357 ftp_queue(ftp, FTP_CDUP, 0);
359 if(chdir("..") == 0) {
360 getcwd(curdir, sizeof curdir);
367 sel = tui_get_list_sel(uilist[focus]);
369 struct ftp_transfer *xfer;
370 struct ftp_dirent *ent = ftp_dirent(ftp, sel);
371 char *lname = alloca(strlen(ent->name) + 1);
373 fixname(lname, ent->name);
375 xfer = malloc_nf(sizeof *xfer);
376 if(!(xfer->fp = fopen(lname, "wb"))) {
377 errmsg("failed to open %s: %s\n", lname, strerror(errno));
383 xfer->rname = strdup_nf(ent->name);
384 xfer->total = ent->size;
386 xfer->done = xfer_done;
390 ftp_queue_transfer(ftp, xfer);
398 sel = tui_get_list_sel(uilist[focus]);
401 struct ftp_dirent *ent = localdir + sel;
402 if(ent->type == FTP_FILE) {
403 infomsg("removing local file: %s\n", ent->name);
418 static void fixname(char *dest, const char *src)
426 if((suffix = strrchr(dest, '.'))) {
428 if(strlen(suffix) > 3) {
432 if((len = strlen(dest)) > 8) {
439 if(dest + len != suffix) {
440 memmove(dest + len, suffix, strlen(suffix) + 1);
447 static void xfer_done(struct ftp *ftp, struct ftp_transfer *xfer)
459 static const char *usage = "Usage: %s [options] [hostname] [port]\n"
461 " -h: print usage information and exit\n";
463 int parse_args(int argc, char **argv)
467 for(i=1; i<argc; i++) {
468 if(argv[i][0] == '-') {
469 if(argv[i][2] != 0) {
474 printf(usage, argv[0]);
488 if((port = atoi(argv[i])) <= 0) {
489 fprintf(stderr, "invalid port number: %s\n", argv[i]);
502 fprintf(stderr, "invalid argument: %s\n", argv[i]);
503 fprintf(stderr, usage, argv[0]);
511 void detect_lfn(void)
513 union REGS regs = {0};
514 struct SREGS sregs = {0};
515 unsigned int drv, buf_seg;
520 if(_dos_allocmem(3, &buf_seg) != 0) {
524 #error TODO port to 16bit
526 buf = (char*)(buf_seg << 4);
529 sprintf(buf, "%c:\\", 'A' + drv);
531 sregs.ds = sregs.es = buf_seg;
533 regs.w.dx = 0; /* ds:dx drive letter string */
534 regs.w.di = 4; /* es:di buffer for filesystem name */
535 regs.w.cx = 44; /* buffer size (3*16 - 4) */
537 intdosx(®s, ®s, &sregs);
539 if(regs.w.cflag == 0) {
540 infomsg("long filenames available\n");
543 infomsg("long filenames not available, will truncate as needed\n");
546 _dos_freemem(buf_seg);