#define fcntlsocket fcntl
#endif
-#define TIMEOUT 5
+#define TIMEOUT 15
static int newconn(struct ftp *ftp);
static int sendcmd(struct ftp *ftp, const char *fmt, ...);
{
struct ftp_op *fop;
- if(!ftp->qhead) {
+ if(!ftp->busy && !ftp->qhead) {
exec_op(ftp, op, arg);
return 0;
}
if(!(fop = malloc(sizeof *fop))) {
return -1;
}
- if(!(fop->arg = strdup(arg))) {
- free(fop);
- return -1;
+ if(arg) {
+ if(!(fop->arg = strdup(arg))) {
+ free(fop);
+ return -1;
+ }
+ } else {
+ fop->arg = 0;
}
fop->op = op;
fop->next = 0;
- ftp->qtail->next = fop;
- ftp->qtail = fop;
+ if(ftp->qhead) {
+ ftp->qtail->next = fop;
+ ftp->qtail = fop;
+ } else {
+ ftp->qhead = ftp->qtail = fop;
+ }
return 0;
}
return -1;
}
+ ftp->busy = 1;
+
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
if(ftp->cproc) {
if(ftp->cproc(ftp, code, buf, ftp->cproc_cls) <= 0) {
ftp->cproc = 0;
+ ftp->busy = 0;
/* execute next operation if there's one queued */
exec_queued(ftp);
errmsg("login failed\n");
break;
}
+ ftp->busy = 0;
}
static int newconn(struct ftp *ftp)
memcpy(ent->name, ptr, len);
ent->name[len] = 0;
- infomsg("name: %s\n", ent->name);
return 0;
}
tail = ent;
}
}
- while(*ptr && *ptr != '\n' && *ptr != '\r') ptr++;
- while(*ptr && (*ptr == '\r' || *ptr == '\n')) ptr++;
+ while(ptr < end && *ptr != '\n' && *ptr != '\r') ptr++;
+ while(ptr < end && (*ptr == '\r' || *ptr == '\n')) ptr++;
}
ftp->modified |= FTP_MOD_REMDIR;
int ctl, lis, data; /* sockets */
int lis_port;
- int status;
+ int status, busy;
char *user, *pass;
struct ftp_op *qhead, *qtail;
#define INPUT_H_
enum {
+ KB_DEL = 128,
+ KB_INS,
+ KB_UP,
+ KB_DOWN,
+ KB_LEFT,
+ KB_RIGHT,
+ KB_HOME,
+ KB_END,
+ KB_PGUP,
+ KB_PGDN
+};
+
+enum {
EV_KEY,
EV_MMOVE,
EV_MBUTTON
tg_clear();
uilist = tui_list("Remote", 0, 0, 40, 23, 0, 0);
- tui_add_list_item(uilist, "first item");
- tui_add_list_item(uilist, "second item");
- tui_add_list_item(uilist, "another item");
- tui_add_list_item(uilist, "foo");
tg_setcursor(0, 23);
}
}
- if(ftp->modified) {
- updateui();
- ftp->modified = 0;
- }
+ updateui();
}
tg_cleanup();
return 0;
}
+static int cmpnames(const void *a, const void *b)
+{
+ const char *sa = *(const char**)a;
+ const char *sb = *(const char**)b;
+ infomsg("cmp(%s, %s)\n", sa, sb);
+ return strcmp(sa, sb);
+}
+
void updateui(void)
{
struct ftp_dirent *ent;
unsigned int upd = 0;
+ char buf[128];
if(ftp->curdir_rem && strcmp(tui_get_title(uilist), ftp->curdir_rem) != 0) {
tui_set_title(uilist, ftp->curdir_rem);
if(ftp->modified & FTP_MOD_REMDIR) {
tui_clear_list(uilist);
- ent = ftp->curdir_rem;
+ ent = ftp->dent_rem;
while(ent) {
- tui_add_list_item(uilist, ent->name);
+ if(ent->type == FTP_DIR) {
+ sprintf(buf, "%s/", ent->name);
+ tui_add_list_item(uilist, buf);
+ } else {
+ tui_add_list_item(uilist, ent->name);
+ }
ent = ent->next;
}
+
+ //tui_sort_list(uilist, cmpnames);
+ tui_list_select(uilist, 0);
+
+ ftp->modified &= ~FTP_MOD_REMDIR;
upd |= 1;
}
- if(upd & 1) {
+ if(tui_isdirty(uilist) || upd & 1) {
tui_draw(uilist);
}
}
case 'q':
return -1;
+ case KB_UP:
+ tui_list_sel_prev(uilist);
+ break;
+ case KB_DOWN:
+ tui_list_sel_next(uilist);
+ break;
+ case KB_LEFT:
+ tui_list_sel_start(uilist);
+ break;
+ case KB_RIGHT:
+ tui_list_sel_end(uilist);
+ break;
+
default:
break;
}
return w->par;
}
+int tui_isdirty(struct tui_widget *w)
+{
+ return w->dirty;
+}
+
void tui_draw(struct tui_widget *w)
{
struct tui_widget *iter;
if(w->cbfunc[TUI_DRAW]) {
w->cbfunc[TUI_DRAW](w, 0);
}
+ w->dirty = 0;
iter = w->child;
while(iter) {
void tui_remove_widget(struct tui_widget *par, struct tui_widget *w);
struct tui_widget *tui_parent(struct tui_widget *w);
+int tui_isdirty(struct tui_widget *w);
void tui_draw(struct tui_widget *w);
void tui_set_callback(struct tui_widget *w, int type, tui_callback func, void *cls);
void tui_clear_list(struct tui_widget *w);
void tui_add_list_item(struct tui_widget *w, const char *text);
+int tui_num_list_items(struct tui_widget *w);
+
+int tui_list_select(struct tui_widget *w, int idx);
+int tui_get_list_sel(struct tui_widget *w);
+int tui_list_sel_next(struct tui_widget *w);
+int tui_list_sel_prev(struct tui_widget *w);
+int tui_list_sel_start(struct tui_widget *w);
+int tui_list_sel_end(struct tui_widget *w);
+
+void tui_sort_list(struct tui_widget *w, int (*cmpfunc)(const void*, const void*));
void tui_wtoscr(struct tui_widget *w, int x, int y, int *retx, int *rety);
void tui_scrtow(struct tui_widget *w, int x, int y, int *retx, int *rety);
#include "tui.h"
#include "tuipriv.h"
#include "tgfx.h"
+#include "util.h"
static void free_list(struct tui_widget *w, void *cls);
static void draw_list(struct tui_widget *w, void *cls);
w->height = height;
w->entries = darr_alloc(0, sizeof *w->entries);
+ w->sel = -1;
+ w->dirty = 1;
if(cbfunc) {
tui_set_callback((struct tui_widget*)w, TUI_ONMODIFY, cbfunc, cbdata);
static void free_list(struct tui_widget *w, void *cls)
{
- darr_free(((struct tui_list*)w)->entries);
+ struct tui_list *wl = (struct tui_list*)w;
+
+ tui_clear_list(w);
+ darr_free(wl->entries);
}
void tui_clear_list(struct tui_widget *w)
{
+ int i;
struct tui_list *wl = (struct tui_list*)w;
assert(wl->type == TUI_LIST);
+
+ for(i=0; i<darr_size(wl->entries); i++) {
+ free(wl->entries[i]);
+ }
darr_clear(wl->entries);
+ wl->dirty = 1;
}
void tui_add_list_item(struct tui_widget *w, const char *text)
{
+ char *str;
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+ str = strdup_nf(text);
+ darr_push(wl->entries, &str);
+ wl->dirty = 1;
+}
+
+int tui_num_list_items(struct tui_widget *w)
+{
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+ return darr_size(wl->entries);
+}
+
+int tui_list_select(struct tui_widget *w, int idx)
+{
+ int offs, nelem;
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ if(idx == wl->sel) {
+ return 0; /* no change */
+ }
+
+ if(idx < 0) {
+ wl->sel = -1;
+ return 0;
+ }
+ if(idx >= (nelem = darr_size(wl->entries))) {
+ return -1;
+ }
+ wl->sel = idx;
+
+ if(idx < wl->view_offs || idx >= wl->view_offs + wl->height) {
+ offs = idx - wl->height / 2;
+ if(offs + wl->height >= nelem) {
+ offs = nelem - wl->height;
+ }
+ if(offs < 0) {
+ offs = 0;
+ }
+ wl->view_offs = offs;
+ }
+
+ wl->dirty = 1;
+ return 0;
+}
+
+int tui_get_list_sel(struct tui_widget *w)
+{
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+ return wl->sel;
+}
+
+int tui_list_sel_next(struct tui_widget *w)
+{
+ int nelem;
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ nelem = darr_size(wl->entries);
+
+ if(wl->sel + 1 >= nelem) {
+ return -1;
+ }
+
+ if(++wl->sel - wl->view_offs >= wl->height) {
+ wl->view_offs = wl->sel - wl->height;
+ }
+ wl->dirty = 1;
+ return 0;
+}
+
+int tui_list_sel_prev(struct tui_widget *w)
+{
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ if(wl->sel <= 0) {
+ return -1;
+ }
+ if(--wl->sel < wl->view_offs) {
+ wl->view_offs = wl->sel;
+ }
+ wl->dirty = 1;
+ return 0;
+}
+
+int tui_list_sel_start(struct tui_widget *w)
+{
struct tui_list *wl = (struct tui_list*)w;
assert(wl->type == TUI_LIST);
- darr_push(wl->entries, &text);
+
+ wl->sel = 0;
+ wl->view_offs = 0;
+ wl->dirty = 1;
+ return 0;
+}
+
+int tui_list_sel_end(struct tui_widget *w)
+{
+ int nelem;
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ nelem = darr_size(wl->entries);
+
+ wl->sel = nelem - 1;
+ wl->view_offs = nelem - wl->height;
+ if(wl->view_offs < 0) wl->view_offs = 0;
+ wl->dirty = 1;
+ return 0;
+}
+
+void tui_sort_list(struct tui_widget *w, int (*cmpfunc)(const void*, const void*))
+{
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ if(!cmpfunc) {
+ cmpfunc = (int (*)(const void*, const void*))strcmp;
+ }
+ qsort(wl->entries, darr_size(wl->entries), sizeof *wl->entries, cmpfunc);
}
static void draw_list(struct tui_widget *w, void *cls)
{
- int i, x, y, num;
+ int i, x, y, num, idx;
struct tui_list *wl = (struct tui_list*)w;
tui_wtoscr(w, 0, 0, &x, &y);
tg_bgcolor(TGFX_BLUE);
+ tg_fgcolor(TGFX_CYAN);
tg_rect(wl->title, x, y, wl->width, wl->height, TGFX_FRAME);
num = darr_size(wl->entries);
x++;
for(i=0; i<num; i++) {
- tg_text(x, ++y, "%s", wl->entries[i]);
+ idx = i + wl->view_offs;
+ if(idx == wl->sel) {
+ tg_bgcolor(TGFX_CYAN);
+ tg_fgcolor(TGFX_BLUE);
+
+ tg_rect(0, x, ++y, wl->width-2, 1, 0);
+ tg_text(x, y, "%s", wl->entries[idx]);
+
+ tg_bgcolor(TGFX_BLUE);
+ tg_fgcolor(TGFX_CYAN);
+ } else {
+ tg_text(x, ++y, "%s", wl->entries[idx]);
+ }
}
}
int type; \
char *title; \
int x, y, width, height; \
+ int dirty; \
tui_callback cbfunc[TUI_NUM_CALLBACKS]; \
void *cbcls[TUI_NUM_CALLBACKS]; \
struct tui_widget *par, *child, *next
struct tui_list {
WCOMMON;
char **entries; /* darr */
+ int sel;
+ int view_offs;
};
int init_input(void)
{
- nodelay(stdscr, TRUE);
return 0;
}
{
}
+static int convkey(int key)
+{
+ switch(key) {
+ case KEY_DC:
+ return KB_DEL;
+ case KEY_IC:
+ return KB_INS;
+ case KEY_UP:
+ return KB_UP;
+ case KEY_DOWN:
+ return KB_DOWN;
+ case KEY_LEFT:
+ return KB_LEFT;
+ case KEY_RIGHT:
+ return KB_RIGHT;
+ case KEY_HOME:
+ return KB_HOME;
+ case KEY_END:
+ return KB_END;
+ case KEY_PPAGE:
+ return KB_PGUP;
+ case KEY_NPAGE:
+ return KB_PGDN;
+ default:
+ break;
+ }
+ if(key < 128) {
+ return key;
+ }
+ return -1;
+}
+
int poll_input(union event *ev)
{
ev->type = EV_KEY;
- ev->key.key = getch();
+ if((ev->key.key = convkey(getch())) == -1) {
+ return 0;
+ }
return 1;
}
#include <curses.h>
#include "tgfx.h"
+#include "util.h"
+
+struct cpair {
+ int pair;
+ int fg, bg;
+};
+static struct cpair *colors;
+static int num_colors;
static int fgcol, bgcol;
static int bgchar;
+static int cur_pair;
static int cur_x, cur_y;
static int curses_color(int col);
+static void upd_color(void);
void tg_init(void)
{
initscr();
cbreak();
keypad(stdscr, TRUE);
+ nodelay(stdscr, TRUE);
noecho();
start_color();
bgcol = curses_color(TGFX_BLACK);
bgchar = ' ';
- tg_color(fgcol | (bgcol << 4));
+ colors = malloc_nf(COLORS * sizeof *colors);
+ num_colors = 0;
}
void tg_cleanup(void)
void tg_fgcolor(int col)
{
fgcol = curses_color(col);
- init_pair(1, fgcol, bgcol);
}
void tg_bgcolor(int col)
{
bgcol = curses_color(col);
- init_pair(1, fgcol, bgcol);
}
void tg_color(int col)
{
fgcol = curses_color(col & 0xf);
bgcol = curses_color((col >> 4) & 0xf);
- init_pair(1, fgcol, bgcol);
}
void tg_bgchar(int c)
void tg_vtext(int x, int y, const char *fmt, va_list ap)
{
- attron(COLOR_PAIR(1));
+ upd_color();
+ attron(COLOR_PAIR(cur_pair));
move(y, x);
vw_printw(stdscr, fmt, ap);
- attroff(COLOR_PAIR(1));
+ attroff(COLOR_PAIR(cur_pair));
}
{
int i;
- attron(COLOR_PAIR(1));
+ upd_color();
+ attron(COLOR_PAIR(cur_pair));
for(i=0; i<ysz; i++) {
move(y + i, x);
tg_text(x + 2, y, "%s", label);
}
- attroff(COLOR_PAIR(1));
+ attroff(COLOR_PAIR(cur_pair));
}
static int curses_color(int col)
}
return col;
}
+
+static void upd_color(void)
+{
+ int i;
+
+ for(i=0; i<num_colors; i++) {
+ if(fgcol == colors[i].fg && bgcol == colors[i].bg) {
+ cur_pair = colors[i].pair;
+ return;
+ }
+ }
+
+ /* not found, allocate a new color pair */
+ if(num_colors >= COLORS) {
+ return;
+ }
+ i = num_colors++;
+ cur_pair = num_colors;
+
+ colors[i].fg = fgcol;
+ colors[i].bg = bgcol;
+ colors[i].pair = cur_pair;
+ init_pair(cur_pair, fgcol, bgcol);
+}