/*
* file.c Functions to handle file selector.
*
* This file is part of the minicom communications package,
* Copyright 1991-1995 Miquel van Smoorenburg.
*
* This file created from code mostly cadged from "dial.c"
* by Miquel van Smoorenburg. Written by James S. Seymour.
* Copyright (c) 1998 by James S. Seymour (jseymour@jimsun.LinxNet.com)
* Some mods for i18n
* Copyright (c) 1998 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <limits.h>
#include "port.h"
#include "minicom.h"
#include "intl.h"
#include "getsdir.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#define FILE_MWTR 1 /* main window top row */
#define SUBM_OKAY 5 /* last entry in sub-menu */
static int nrents = 1;
static void file_tell(const char *s);
static void dhili(int k);
static void prdir(WIN *dirw, int top, int cur, GETSDIR_ENTRY *dirdat, int longest);
static void prone(WIN *dirw, GETSDIR_ENTRY *dirdat, int longest, int inverse);
static void *set_work_dir(void *existing, size_t min_len);
static int new_filedir(GETSDIR_ENTRY *dirdat, int flushit);
static void goto_filedir(char *new_dir, int absolut);
static int tag_untag(char *pat, int tag);
static char *concat_list(GETSDIR_ENTRY *d);
static WIN *dsub;
static const char *const what[] =
{
/* TRANSLATORS: Translation of each of these menu items should not be
* longer than 7 characters. The upper-case letter is a shortcut,
* so keep them unique and ASCII; 'h', 'j', 'k', 'l' are reserved */
N_("[Goto]"), N_("[Prev]"), N_("[Show]"), N_("[Tag]"), N_("[Untag]"),
N_("[Okay]")
};
#define WHAT_NR_OPTIONS (sizeof (what) / sizeof (*what))
#define WHAT_WIDTH 8 /* Width of one entry */
/* Number of bytes for <= 7 characters */
static int what_lens[WHAT_NR_OPTIONS];
/* Number of ' ' padding entries at left and right, left is >= 1 */
static int what_padding[WHAT_NR_OPTIONS][2];
static int dprev;
/* Little menu. */
static const char *d_yesno[] = { N_(" Yes "), N_(" No "), NULL };
/*
* Tell a little message
*/
static void file_tell(const char *s)
{
WIN *w;
w = mc_tell("%s", s);
sleep(1);
mc_wclose(w, 1);
}
/* Draw an entry in the horizontal menu */
static void horiz_draw(size_t k, char start_attr, char end_attr)
{
static const char spaces[] = " ";
mc_wprintf(dsub, "%.*s", what_padding[k][0], spaces);
mc_wsetattr(dsub, start_attr);
mc_wprintf(dsub, "%.*s", what_lens[k], _(what[k]));
mc_wsetattr(dsub, end_attr);
mc_wprintf(dsub, "%.*s", what_padding[k][1], spaces);
}
/*
* Highlight a choice in the horizontal menu.
*/
static void dhili(int k)
{
int initial_y = (76 - (WHAT_NR_OPTIONS * WHAT_WIDTH >= 76
? 74 : WHAT_NR_OPTIONS * WHAT_WIDTH)) / 2;
if (k == dprev)
return;
if (dprev >= 0) {
mc_wlocate(dsub, initial_y + WHAT_WIDTH * dprev, 0);
if (!useattr)
mc_wputs(dsub, " ");
else
horiz_draw(dprev, stdattr, stdattr);
}
dprev = k;
mc_wlocate(dsub, initial_y + WHAT_WIDTH * k, 0);
if (!useattr)
mc_wputs(dsub, ">");
else
horiz_draw(k, XA_REVERSE | stdattr, stdattr);
}
static inline GETSDIR_ENTRY *getno(int no, GETSDIR_ENTRY *d)
{
if (no >= nrents)
return NULL;
return d + no;
}
/*
* Print the directory. Only draw from "cur" to bottom.
*/
static void prdir(WIN *dirw, int top, int cur,
GETSDIR_ENTRY *dirdat, int longest)
{
int f, start;
char f_str[BUFSIZ];
char t_str[BUFSIZ];
start = cur - top;
dirflush = 0;
sprintf(f_str, " %%-%ds", longest + 2);
mc_wlocate(dirw, 0, start + FILE_MWTR);
for (f = start; f < dirw->ys - (1 + FILE_MWTR); f++) {
GETSDIR_ENTRY *d;
if (!(d = getno(f + top, dirdat)))
break;
if (d->cflags & FL_TAG)
mc_wsetattr(dirw, XA_REVERSE | stdattr);
if (S_ISDIR(d->mode)) {
snprintf(t_str, sizeof(t_str), "[%s]", d->fname);
mc_wprintf(dirw, f_str, t_str);
} else
mc_wprintf(dirw, f_str, d->fname);
mc_wsetattr(dirw, XA_NORMAL | stdattr);
mc_wputc(dirw, '\n');
}
dirflush = 1;
mc_wflush();
}
/*
* Print one directory entry.
*/
static void prone(WIN *dirw, GETSDIR_ENTRY *dirdat, int longest, int inverse)
{
char f_str[BUFSIZ];
char t_str[BUFSIZ];
dirflush = 0;
sprintf(f_str, " %%-%ds", longest + 2);
/*
if (dirdat->cflags & FL_TAG)
mc_wsetattr(dirw, XA_REVERSE | stdattr);
*/
if (inverse)
mc_wsetattr(dirw, XA_REVERSE | stdattr);
if (S_ISDIR(dirdat->mode)) {
snprintf(t_str, sizeof(t_str), "[%s]", dirdat->fname);
mc_wprintf(dirw, f_str, t_str);
} else
mc_wprintf(dirw, f_str, dirdat->fname);
mc_wsetattr(dirw, XA_NORMAL | stdattr);
dirflush = 1;
mc_wflush();
}
static WIN *main_w;
static GETSDIR_ENTRY *global_dirdat;
static int cur = 0;
static int ocur = 0;
static int subm = SUBM_OKAY;
static int quit = 0;
static int top = 0;
static int c = 0;
static int pgud = 0;
static int first = 1;
static char *s;
static int longest;
static char file_title[BUFSIZ];
static char cwd_str[BUFSIZ];
static char *prev_dir = NULL;
static char *work_dir = NULL;
static char *d_work_dir = NULL;
static char *u_work_dir = NULL;
static int min_len = 1;
static char wc_str[128] = "";
static char wc_mem[128] = "";
static int tag_cnt = 0;
static int how_many = 0;
static int down_loading = 0;
static char *ret_buf = NULL;
/* Init up/down work directories to make sure we start from
* the configuration defaults on the next up/download. jl 6/2000
*/
void init_dir(char dir)
{
char *p = NULL;
switch (dir) {
case 'u':
p = u_work_dir;
u_work_dir = NULL;
break;
case 'd':
p = d_work_dir;
d_work_dir = NULL;
break;
}
free((void *) p);
return;
}
static void *set_work_dir(void *existing, size_t min_len)
{
void *vp = realloc(existing, min_len);
if (down_loading)
d_work_dir = vp;
else
u_work_dir = vp;
return vp;
}
/*
* Initialize new file directory.
*
* Sets the current working directory. Non-0 return = no change.
*/
static int new_filedir(GETSDIR_ENTRY *dirdat, int flushit)
{
static size_t dp_len = 0;
static char cwd_str_fmt[BUFSIZ] = "";
size_t new_dp_len, fmt_len;
char disp_dir[80];
int initial_y = (76 - (WHAT_NR_OPTIONS * WHAT_WIDTH >= 76
? 74 : WHAT_NR_OPTIONS * WHAT_WIDTH)) / 2;
size_t i;
char * new_prev_dir;
cur = 0;
ocur = 0;
subm = SUBM_OKAY;
quit = 0;
top = 0;
c = 0;
pgud = 0;
first = 1;
min_len = 1;
dprev = -1;
tag_cnt = 0;
/*
* get last directory
*/
work_dir = down_loading ? d_work_dir : u_work_dir;
/*
* init working directory to default?
*/
if (work_dir == NULL) {
char *s = down_loading? P_DOWNDIR : P_UPDIR;
min_len = 1;
if (*s != '/')
min_len += strlen(homedir) + 1;
min_len += strlen(s);
if (min_len < BUFSIZ)
min_len = BUFSIZ;
work_dir = set_work_dir(NULL, min_len);
if (*s == '/')
strncpy(work_dir, s, min_len);
else
snprintf(work_dir, min_len, "%s/%s", homedir, s);
}
/* lop-off trailing "/" for consistency */
if (strlen(work_dir) > 1 && work_dir[strlen(work_dir) - 1] == '/')
work_dir[strlen(work_dir) - 1] = (char)0;
/* get the current working directory, which will become the prev_dir, on success */
new_prev_dir = getcwd(NULL, BUFSIZ);
if (!new_prev_dir)
return -1;
if (!access(work_dir, R_OK | X_OK) && !chdir(work_dir)) {
/* was able to change to new working directory */
free(prev_dir);
prev_dir = new_prev_dir;
}
else {
/* Could not change to the new working directory */
mc_wbell();
werror(
_("Could not change to directory %s (%s)"),
work_dir,
strerror(errno));
/* restore the previous working directory */
free(work_dir);
work_dir = set_work_dir(new_prev_dir, strlen(new_prev_dir));
}
/* All right, draw the file directory! */
if (flushit) {
dirflush = 0;
mc_winclr(main_w);
mc_wredraw(main_w, 1);
}
mc_wcursor(main_w, CNORMAL);
{
char *s;
if (down_loading) {
if (how_many < 0)
s = _("Select one or more files for download");
else if (how_many)
s = _("Select a file for download");
else
s = _("Select a directory for download");
} else {
if (how_many < 0)
s = _("Select one or more files for upload");
else if (how_many)
s = _("Select a file for upload");
else
s = _("Select a directory for upload");
}
snprintf(file_title, sizeof(file_title), "%s", s);
}
mc_wtitle(main_w, TMID, file_title);
if ((new_dp_len = strlen(work_dir)) > dp_len) {
dp_len = new_dp_len;
snprintf(cwd_str_fmt, sizeof(cwd_str_fmt),
_("Directory: %%-%ds"), (int)dp_len);
}
new_dp_len = mbslen (work_dir);
if (new_dp_len + (fmt_len = mbslen(cwd_str_fmt)) > 75) {
size_t i;
char *tmp_dir = work_dir;
/* We want the last 73 characters */
for (i = 0; 73 + i < new_dp_len + fmt_len; i++) {
wchar_t wc;
tmp_dir += one_mbtowc(&wc, work_dir, MB_LEN_MAX);
}
snprintf(disp_dir, sizeof(disp_dir), "...%s", tmp_dir);
snprintf(cwd_str, sizeof(cwd_str), cwd_str_fmt, disp_dir);
} else
snprintf(cwd_str, sizeof(cwd_str), cwd_str_fmt, work_dir);
mc_wlocate(main_w, 0, 0);
mc_wputs(main_w, cwd_str);
for (i = 0; i < WHAT_NR_OPTIONS; i++) {
const char *str, *c;
size_t j;
str = _(what[i]);
c = str;
for (j = 0; j < WHAT_WIDTH - 1 && *c != 0; j++) {
wchar_t wc;
c += one_mbtowc (&wc, c, MB_LEN_MAX);
}
what_lens[i] = c - str;
j = WHAT_WIDTH - j; /* Characters left for padding */
what_padding[i][1] = j / 2; /* Rounding down */
what_padding[i][0] = j - what_padding[i][1]; /* >= 1 */
}
mc_wlocate(dsub, initial_y, 0);
for (i = 0; i < WHAT_NR_OPTIONS; i++)
horiz_draw(i, mc_wgetattr(dsub), mc_wgetattr(dsub));
mc_wsetregion(main_w, 1, main_w->ys - FILE_MWTR);
main_w->doscroll = 0;
/* old dir to discard? */
free(dirdat);
dirdat = NULL;
/* get sorted directory */
if ((nrents = getsdir(".", wc_str,
GETSDIR_PARNT|GETSDIR_NSORT|GETSDIR_DIRSF,
0, &dirdat, &longest)) < 0) {
/* we really want to announce the error here!!! */
mc_wclose(main_w, 1);
mc_wclose(dsub, 1);
free(dirdat);
dirdat = NULL;
return -1;
}
global_dirdat = dirdat; // Hmm...
prdir(main_w, top, top, dirdat, longest);
mc_wlocate(main_w, initial_y, main_w->ys - FILE_MWTR);
mc_wputs(main_w, _("( Escape to exit, Space to tag )"));
dhili(subm);
/* this really needs to go in dhili !!!*/
mc_wlocate(main_w, 0, cur + FILE_MWTR - top);
if (flushit) {
dirflush = 1;
mc_wredraw(dsub, 1);
}
return 0;
}
/*
* Goto a new directory
*/
static void goto_filedir(char *new_dir, int absolut)
{
if (strcmp(new_dir, "..") == 0) {
if (strcmp(work_dir, "/")) {
char *sp = strrchr(work_dir, '/');
*sp = (char)0;
if (strlen(work_dir) == 0)
strcpy(work_dir, "/");
} else {
file_tell(_("Can't back up!"));
return;
}
} else if (!absolut) {
int new_len = strlen(work_dir) + 1; /* + '/' */
if ((new_len += strlen(new_dir) + 1) > min_len) {
min_len = new_len;
work_dir = set_work_dir(work_dir, min_len);
}
if (strcmp(work_dir, "/") != 0)
strcat(work_dir, "/");
strcat(work_dir, new_dir);
} else {
int new_len = 1;
if (*new_dir != '/')
new_len += strlen(homedir) + 1;
new_len += strlen(new_dir);
if (min_len < new_len)
min_len = new_len;
work_dir = set_work_dir(work_dir, min_len);
if (*new_dir == '/')
strncpy(work_dir, new_dir, min_len);
else
snprintf(work_dir, min_len, "%s/%s", homedir, new_dir);
}
new_filedir(global_dirdat, 1);
}
/*
* Initialize the file directory.
*/
static int init_filedir(void)
{
int x1, x2;
int retstat = -1;
dirflush = 0;
x1 = (COLS / 2) - 37;
x2 = (COLS / 2) + 37;
dsub = mc_wopen(x1 - 1, LINES - 3, x2 + 1, LINES - 3, BNONE,
stdattr, mfcolor, mbcolor, 0, 0, 1);
main_w = mc_wopen(x1, 2, x2, LINES - 6, BSINGLE, stdattr, mfcolor,
mbcolor, 0, 0, 1);
if (ret_buf != NULL ||
(retstat = ((ret_buf = (char *)malloc(BUFSIZ)) == NULL)? -1 : 0) == 0) {
retstat = new_filedir(NULL, 0);
dirflush = 1;
mc_wredraw(dsub, 1);
}
return retstat;
}
static int tag_untag(char *pat, int tag)
{
GETSDIR_ENTRY *d = global_dirdat;
int indxr, cntr;
if (nrents < 1)
return 0;
for (indxr = nrents, cntr = 0; indxr; --indxr, ++d)
if (S_ISREG(d->mode) && wildmat(d->fname, pat)) {
if (tag) {
d->cflags |= FL_TAG;
++cntr;
} else if (d->cflags & FL_TAG) {
d->cflags &= ~FL_TAG;
++cntr;
}
}
return cntr;
}
/*
* concatenate tagged files into a buffer
*/
static char *concat_list(GETSDIR_ENTRY *dirdat)
{
GETSDIR_ENTRY *d;
int indxr, len;
int i;
char *j;
d = dirdat;
for (indxr = nrents, len = 0; indxr; --indxr, ++d)
if (d->cflags & FL_TAG)
len += strlen(d->fname) + 1;
if (len) {
if (len > BUFSIZ) {
if ((ret_buf = (char *)realloc(ret_buf, len)) == NULL) {
file_tell(_("Too many files tagged - buffer would overflow"));
return NULL;
}
}
*ret_buf = (char)0;
d = dirdat;
for (indxr = nrents; indxr; --indxr, ++d)
if (d->cflags & FL_TAG) {
/* this could be *much* more efficient */
for (i = strlen(ret_buf), j = d->fname; *j; j++) {
if (*j == ' ') {
if ((ret_buf = (char*)realloc(ret_buf, ++len)) == NULL) {
file_tell(_("Too many files tagged - buffer would overflow"));
return(NULL);
}
ret_buf[i++] = '\\';
}
ret_buf[i++] = *j;
}
ret_buf[i++] = ' ';
ret_buf[i] = '\0';
}
ret_buf[strlen(ret_buf) - 1] = (char)0;
return ret_buf;
}
return NULL;
}
/*
* Draw the file directory.
*
* howmany - How many files can be selected
* 0 = none (for directory selection only, as in "rz")
* 1 = one (for single-file up-/down-loads, as in "rx")
* -1 = any number (for multiple files, as in "sz")
*
* downloading - Is this for download selection?
* 0 = no
* 1 = yes - when single file selected, see if it exists
*/
char * filedir(int howmany, int downloading)
{
time_t click_time = (time_t) 0;
size_t i;
how_many = howmany;
down_loading = downloading;
init_filedir();
again:
mc_wlocate(main_w, 0, cur + FILE_MWTR - top);
if (first) {
mc_wredraw(main_w, 1);
first = 0;
}
while (!quit) {
GETSDIR_ENTRY *d = getno(cur, global_dirdat);
/*
if(S_ISDIR(d->mode))
prone(main_w, d, longest, 0);
*/
switch (c = wxgetch()) {
case K_UP:
case 'k':
/*
if(S_ISDIR(d->mode))
prone(main_w, d, longest, 1);
*/
cur -= cur > 0;
break;
case K_DN:
case 'j':
/*
if(S_ISDIR(d->mode))
prone(main_w, d, longest, 1);
*/
cur += cur < nrents - 1;
break;
case K_LT:
case 'h':
subm--;
if (subm < 0)
subm = SUBM_OKAY;
break;
case K_RT:
case 'l':
subm = (subm + 1) % 6;
break;
case K_PGUP:
case '\002': /* Control-B */
pgud = 1;
quit = 1;
break;
case K_PGDN:
case '\006': /* Control-F */
pgud = 2;
quit = 1;
break;
case ' ': /* Tag if not directory */
if (S_ISDIR(d->mode)) {
time_t this_time = time((time_t *)NULL);
if (this_time - click_time < 2) {
GETSDIR_ENTRY *d2 = getno(cur, global_dirdat);
goto_filedir(d2->fname, 0);
click_time = (time_t)0;
} else
click_time = this_time;
}
else {
if (how_many) {
if ((d->cflags ^= FL_TAG) & FL_TAG) {
if (tag_cnt && how_many == 1) {
d->cflags &= ~FL_TAG;
file_tell(_("Can select only one!"));
break;
}
++tag_cnt;
} else
--tag_cnt;
mc_wlocate(main_w, 0, cur + FILE_MWTR - top);
prone(main_w, d, longest, d->cflags & FL_TAG);
mc_wputc(main_w, '\n');
cur += cur < nrents - 1;
}
}
break;
case '\033':
case '\r':
case '\n':
quit = 1;
break;
default:
for (i = 0; i < WHAT_NR_OPTIONS; i++) {
if (strchr (_(what[i]), toupper (c)) != NULL) {
subm = i;
c = '\n';
quit = 1;
break;
}
}
break;
}
if (c != ' ')
click_time = (time_t)0;
if (cur < top) {
top--;
prdir(main_w, top, top, global_dirdat, longest);
}
if (cur - top > main_w->ys - (2 + FILE_MWTR)) {
top++;
prdir(main_w, top, top, global_dirdat, longest);
}
/*
if(cur != ocur)
mc_wlocate(main_w, 0, cur + FILE_MWTR - top);
*/
ocur = cur;
dhili(subm);
/* this really needs to go in dhili !!!*/
mc_wlocate(main_w, 0, cur + FILE_MWTR - top);
}
quit = 0;
/* ESC means quit */
if (c == '\033') {
mc_wclose(main_w, 1);
mc_wclose(dsub, 1);
free(global_dirdat);
global_dirdat = NULL;
return NULL;
}
/* Page up or down ? */
if (pgud == 1) { /* Page up */
ocur = top;
top -= main_w->ys - (1 + FILE_MWTR);
if (top < 0)
top = 0;
cur = top;
pgud = 0;
if (ocur != top)
prdir(main_w, top, cur, global_dirdat, longest);
ocur = cur;
goto again;
}
if (pgud == 2) { /* Page down */
ocur = top;
if (top < nrents - main_w->ys + (1 + FILE_MWTR)) {
top += main_w->ys - (1 + FILE_MWTR);
if (top > nrents - main_w->ys + (1 + FILE_MWTR)) {
top = nrents - main_w->ys + (1 + FILE_MWTR);
}
cur = top;
} else
cur = nrents - 1;
pgud = 0;
if (ocur != top)
prdir(main_w, top, cur, global_dirdat, longest);
ocur = cur;
goto again;
}
if (c =='\r' || c == '\n') {
switch(subm) {
case 0:
/* Goto directory */
{
char buf[128];
char *s;
strncpy(buf, down_loading? P_DOWNDIR : P_UPDIR, sizeof(buf) -1);
s = input(_("Goto directory:"), buf);
/* if(s == NULL || *s == (char) 0) */
if (s == NULL)
break;
goto_filedir(buf, 1);
}
break;
case 1:
/* Previous directory */
goto_filedir(prev_dir, 1);
break;
case 2:
/* File (wildcard) spec */
{
char *s = input(_("Filename pattern:"), wc_mem);
if (s == NULL || *s == (char) 0)
break;
strcpy(wc_str, wc_mem);
new_filedir(global_dirdat, 1);
wc_str[0] = (char)0;
}
break;
case 3:
/* Tag */
if (how_many == 1)
file_tell(_("Can select only one!"));
else if (how_many == -1) {
char tag_buf[128];
char *s;
strncpy(tag_buf, wc_mem, 128);
s = input(_("Tag pattern:"), tag_buf);
if (s != NULL && *s != (char)0) {
int newly_tagged;
if ((newly_tagged = tag_untag(tag_buf, 1)) == 0) {
file_tell(_("No file(s) tagged"));
goto tag_end;
}
tag_cnt += newly_tagged;
prdir(main_w, top, top, global_dirdat, longest);
}
}
tag_end:
break;
case 4:
/* Untag */
{
char tag_buf[128];
char *s;
int untagged;
strncpy(tag_buf, wc_mem, 128);
s = input(_("Untag pattern:"), tag_buf);
if (s == NULL || *s == (char)0)
goto untag_end;
if ((untagged = tag_untag(tag_buf, 0)) == 0) {
file_tell(_("No file(s) untagged"));
goto untag_end;
}
tag_cnt -= untagged;
prdir(main_w, top, top, global_dirdat, longest);
}
untag_end:
break;
case 5:
{
/* Done */
char *ret_ptr = NULL; /* failsafe: assume failure */
if (how_many != 0 && !tag_cnt) {
while (1) {
s = input(_("No file selected - enter filename:"),
ret_buf);
if (s != NULL && *s != (char) 0) {
int f_exist = access(ret_buf, F_OK);
if (down_loading) {
if (f_exist != -1) {
/* ask 'em if they're *sure* */
char buf[BUFSIZ];
snprintf(buf, sizeof(buf),
_("File: \"%s\" exists! Overwrite?"), ret_buf);
if (ask(buf, d_yesno) == 0) {
ret_ptr = ret_buf;
break;
}
} else {
ret_ptr = ret_buf;
break;
}
} else {
if (f_exist == -1)
file_tell(_("no such file!"));
else {
ret_ptr = ret_buf;
break;
}
}
} else {
/* maybe want to ask: "abort?", here */
goto again;
}
}
}
else {
/* put 'em in a buffer for return */
if (how_many == 0) {
/* current working directory */
ret_ptr = work_dir;
} else {
ret_ptr = concat_list(global_dirdat);
}
}
mc_wclose(main_w, 1);
mc_wclose(dsub, 1);
free(global_dirdat);
global_dirdat = NULL;
return ret_ptr;
}
break;
default:
/* should "beep", I guess (? shouldn't get here) */
file_tell("BEEP!");
break;
} /* switch */
}
goto again;
}