9b4f9d528f53d54e18f343b8859b8c49af3a5b5e
[oftp] / src / main.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <signal.h>
4 #include <errno.h>
5 #include <assert.h>
6 #ifdef __MSDOS__
7 #include <direct.h>
8 #else
9 #include <unistd.h>
10 #include <dirent.h>
11 #endif
12 #include <sys/stat.h>
13 #include <sys/select.h>
14 #include "tgfx.h"
15 #include "input.h"
16 #include "util.h"
17 #include "tui.h"
18 #include "ftp.h"
19 #include "darray.h"
20
21 void updateui(void);
22 int update_localdir(void);
23 int proc_input(void);
24 int keypress(int key);
25 int parse_args(int argc, char **argv);
26
27 static struct ftp *ftp;
28 static struct tui_widget *uilist[2];
29
30 static int focus;
31
32 static char *host = "localhost";
33 static int port = 21;
34
35 static char curdir[PATH_MAX + 1];
36 static struct ftp_dirent *localdir;
37
38
39 int main(int argc, char **argv)
40 {
41         int i, numsock, maxfd;
42         int ftpsock[16];
43         fd_set rdset;
44         struct timeval tv;
45
46         if(parse_args(argc, argv) == -1) {
47                 return 1;
48         }
49
50         localdir = darr_alloc(0, sizeof *localdir);
51         getcwd(curdir, sizeof curdir);
52         update_localdir();
53
54         if(!(ftp = ftp_alloc())) {
55                 return 1;
56         }
57         if(ftp_connect(ftp, host, port) == -1) {
58                 ftp_free(ftp);
59                 return 1;
60         }
61
62         init_input();
63
64         tg_init();
65
66         tg_bgchar(' ');
67         tg_clear();
68
69         uilist[0] = tui_list("Remote", 0, 0, 40, 23, 0, 0);
70         uilist[1] = tui_list("Local", 40, 0, 40, 23, 0, 0);
71         focus = 0;
72         tui_focus(uilist[focus], 1);
73
74         tg_setcursor(0, 23);
75
76         tui_draw(uilist[0]);
77         tui_draw(uilist[1]);
78
79         for(;;) {
80                 FD_ZERO(&rdset);
81                 maxfd = 0;
82
83                 numsock = ftp_sockets(ftp, ftpsock, sizeof ftpsock);
84                 for(i=0; i<numsock; i++) {
85                         FD_SET(ftpsock[i], &rdset);
86                         if(ftpsock[i] > maxfd) maxfd = ftpsock[i];
87                 }
88
89 #ifdef __unix__
90                 FD_SET(0, &rdset);
91                 tv.tv_sec = 120;
92                 tv.tv_usec = 0;
93 #else
94                 tv.tv_sec = tv.tv_usec = 0;
95 #endif
96
97                 if(select(maxfd + 1, &rdset, 0, 0, &tv) == -1 && errno == EINTR) {
98                         continue;
99                 }
100
101 #ifdef __unix__
102                 if(FD_ISSET(0, &rdset)) {
103                         if(proc_input() == -1) {
104                                 break;
105                         }
106                 }
107 #endif
108
109                 for(i=0; i<numsock; i++) {
110                         if(FD_ISSET(ftpsock[i], &rdset)) {
111                                 ftp_handle(ftp, ftpsock[i]);
112                         }
113                 }
114
115                 updateui();
116         }
117
118         tg_cleanup();
119         cleanup_input();
120         ftp_close(ftp);
121         ftp_free(ftp);
122         return 0;
123 }
124
125 void updateui(void)
126 {
127         int i, num;
128         struct ftp_dirent *ent;
129         unsigned int upd = 0;
130         char buf[128];
131         const char *remdir;
132
133         remdir = ftp_curdir(ftp);
134         if(remdir && strcmp(tui_get_title(uilist[0]), remdir) != 0) {
135                 tui_set_title(uilist[0], remdir);
136                 upd |= 1;
137         }
138
139         if(ftp->modified & FTP_MOD_DIR) {
140                 tui_clear_list(uilist[0]);
141
142                 num = ftp_num_dirent(ftp);
143                 for(i=0; i<num; i++) {
144                         ent = ftp_dirent(ftp, i);
145                         if(ent->type == FTP_DIR) {
146                                 sprintf(buf, "%s/", ent->name);
147                                 tui_add_list_item(uilist[0], buf);
148                         } else {
149                                 tui_add_list_item(uilist[0], ent->name);
150                         }
151                 }
152
153                 tui_list_select(uilist[0], 0);
154
155                 ftp->modified &= ~FTP_MOD_DIR;
156                 upd |= 1;
157         }
158
159         if(strcmp(tui_get_title(uilist[1]), curdir) != 0) {
160                 tui_clear_list(uilist[1]);
161                 num = darr_size(localdir);
162                 for(i=0; i<num; i++) {
163                         ent = localdir + i;
164                         if(ent->type == FTP_DIR) {
165                                 sprintf(buf, "%s/", ent->name);
166                                 tui_add_list_item(uilist[1], buf);
167                         } else {
168                                 tui_add_list_item(uilist[1], ent->name);
169                         }
170                 }
171                 tui_list_select(uilist[1], 0);
172                 tui_set_title(uilist[1], curdir);
173                 upd |= 2;
174         }
175
176         if(tui_isdirty(uilist[0]) || upd & 1) {
177                 tui_draw(uilist[0]);
178         }
179         if(tui_isdirty(uilist[1]) || upd & 2) {
180                 tui_draw(uilist[1]);
181         }
182 }
183
184 int update_localdir(void)
185 {
186         DIR *dir;
187         struct dirent *dent;
188         struct stat st;
189         struct ftp_dirent ent;
190
191         if(!(dir = opendir(curdir))) {
192                 errmsg("failed to open directory: %s\n", curdir);
193                 return -1;
194         }
195
196         darr_clear(localdir);
197         while((dent = readdir(dir))) {
198                 ent.name = strdup_nf(dent->d_name);
199                 if(strcmp(dent->d_name, ".") == 0) continue;
200
201                 if(stat(dent->d_name, &st) == 0) {
202                         if(S_ISDIR(st.st_mode)) {
203                                 ent.type = FTP_DIR;
204                         } else {
205                                 ent.type = FTP_FILE;
206                         }
207                         ent.size = st.st_size;
208                 } else {
209                         ent.type = FTP_FILE;
210                         ent.size = 0;
211                 }
212
213                 darr_push(localdir, &ent);
214         }
215         closedir(dir);
216
217         qsort(localdir, darr_size(localdir), sizeof *localdir, ftp_direntcmp);
218         return 0;
219 }
220
221 int proc_input(void)
222 {
223         union event ev;
224
225         while(poll_input(&ev)) {
226                 switch(ev.type) {
227                 case EV_KEY:
228                         if(keypress(ev.key.key) == -1) {
229                                 return -1;
230                         }
231                         break;
232
233                 default:
234                         break;
235                 }
236         }
237         return 0;
238 }
239
240 int keypress(int key)
241 {
242         int sel;
243
244         switch(key) {
245         case 27:
246         case 'q':
247         case KB_F10:
248                 return -1;
249
250         case '\t':
251                 tui_focus(uilist[focus], 0);
252                 focus ^= 1;
253                 tui_focus(uilist[focus], 1);
254                 break;
255
256         case KB_UP:
257                 tui_list_sel_prev(uilist[focus]);
258                 break;
259         case KB_DOWN:
260                 tui_list_sel_next(uilist[focus]);
261                 break;
262         case KB_LEFT:
263                 tui_list_sel_start(uilist[focus]);
264                 break;
265         case KB_RIGHT:
266                 tui_list_sel_end(uilist[focus]);
267                 break;
268
269         case '\n':
270                 sel = tui_get_list_sel(uilist[focus]);
271                 if(focus == 0) {
272                         struct ftp_dirent *ent = ftp_dirent(ftp, sel);
273                         if(ent->type == FTP_DIR) {
274                                 ftp_queue(ftp, FTP_CHDIR, ent->name);
275                         } else {
276                                 /* TODO */
277                         }
278                 } else {
279                         if(localdir[sel].type == FTP_DIR) {
280                                 if(chdir(localdir[sel].name) == -1) {
281                                         errmsg("failed to change directory: %s\n", localdir[sel].name);
282                                 } else {
283                                         getcwd(curdir, sizeof curdir);
284                                         update_localdir();
285                                 }
286                         } else {
287                                 /* TODO */
288                         }
289                 }
290                 break;
291
292         case '\b':
293                 if(focus == 0) {
294                         ftp_queue(ftp, FTP_CDUP, 0);
295                 } else {
296                         if(chdir("..") == 0) {
297                                 getcwd(curdir, sizeof curdir);
298                                 update_localdir();
299                         }
300                 }
301                 break;
302
303         default:
304                 break;
305         }
306         return 0;
307 }
308
309 static const char *usage = "Usage: %s [options] [hostname] [port]\n"
310         "Options:\n"
311         "  -h: print usage information and exit\n";
312
313 int parse_args(int argc, char **argv)
314 {
315         int i, argidx = 0;
316
317         for(i=1; i<argc; i++) {
318                 if(argv[i][0] == '-') {
319                         if(argv[i][2] != 0) {
320                                 goto inval;
321                         }
322                         switch(argv[i][1]) {
323                         case 'h':
324                                 printf(usage, argv[0]);
325                                 exit(0);
326
327                         default:
328                                 goto inval;
329                         }
330
331                 } else {
332                         switch(argidx++) {
333                         case 0:
334                                 host = argv[i];
335                                 break;
336
337                         case 1:
338                                 if((port = atoi(argv[i])) <= 0) {
339                                         fprintf(stderr, "invalid port number: %s\n", argv[i]);
340                                         return -1;
341                                 }
342                                 break;
343
344                         default:
345                                 goto inval;
346                         }
347                 }
348         }
349
350         return 0;
351 inval:
352         fprintf(stderr, "invalid argument: %s\n", argv[i]);
353         fprintf(stderr, usage, argv[0]);
354         return -1;
355 }