1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00

Merge branch 'elinks-0.12'

Conflicts:
	src/session/download.c
	src/session/download.h
	src/viewer/text/view.c
This commit is contained in:
Kalle Olavi Niemitalo 2009-07-24 18:32:25 +03:00 committed by Kalle Olavi Niemitalo
commit ef548e5728
14 changed files with 606 additions and 165 deletions

View File

@ -231,7 +231,9 @@ PREDEFINED = "LIST_OF(element_T)=element_T list" \
HAVE_SETENV \
HAVE_VARIADIC_MACROS
EXPAND_AS_DEFINED = LIST_HEAD \
INIT_LIST_OF
INIT_LIST_OF \
ACTION_ \
OBJECT_HEAD
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------

View File

@ -29,6 +29,47 @@ struct widget_info_button {
/* Define to find buttons without keyboard accelerator. */
/* #define DEBUG_BUTTON_HOTKEY */
/** @def add_dlg_ok_button
* Add a button that will close the dialog if pressed.
*
* void add_dlg_ok_button(struct dialog *dlg, unsigned char *text, int flags,
* ::done_handler_T *done, void *data);
*
* @param dlg
* The dialog in which the button is to be added.
*
* @param text
* Text displayed in the button. This string should contain a
* keyboard accelerator, marked with a preceding '~'. The pointer
* must remain valid as long as the dialog exists.
*
* @param flags
* Can be ::B_ENTER, ::B_ESC, or 0.
*
* @param done
* A function that BFU calls when the user presses this button.
* Before calling this, BFU checks the values of widgets.
* After the function returns, BFU closes the dialog.
*
* @param data
* A pointer to be passed to the @a done callback. */
/** @def add_dlg_button
* Add a button that need not close the dialog if pressed.
*
* void add_dlg_button(struct dialog *dlg, unsigned char *text, int flags,
* ::widget_handler_T *handler, void *data);
*
* @param handler
* A function that BFU calls when the user presses this button.
* BFU does not automatically check the values of widgets
* or close the dialog.
*
* @param data
* A pointer to any data needed by @a handler. It does not get this
* pointer as a parameter but can read it from widget_data->widget->data.
*
* The other parameters are as in ::add_dlg_ok_button. */
#ifdef DEBUG_BUTTON_HOTKEY
void add_dlg_button_do(const unsigned char *file, int line, struct dialog *dlg, unsigned char *text, int flags, widget_handler_T *handler, void *data, done_handler_T *done, void *done_data);

View File

@ -549,10 +549,11 @@ get_keybinding_action_box_item(enum keymap_id keymap_id, action_id_T action_id)
struct listbox_item *keymap_box_item[KEYMAP_MAX];
void
init_keybinding_listboxes(struct keymap keymap_table[KEYMAP_MAX], struct action_list actions[])
init_keybinding_listboxes(struct keymap keymap_table[KEYMAP_MAX],
const struct action_list actions[])
{
struct listbox_item *root = &keybinding_browser.root;
struct action *act;
const struct action *act;
enum keymap_id keymap_id;
/* Do it backwards because add_listbox_item() add to front
@ -577,7 +578,8 @@ init_keybinding_listboxes(struct keymap keymap_table[KEYMAP_MAX], struct action_
assert(act->desc);
#endif
item = add_listbox_item(NULL, keymap_box, BI_FOLDER, act, -1);
item = add_listbox_item(NULL, keymap_box, BI_FOLDER,
(void *) act, -1);
if (!item) continue;
item->expanded = 1;
@ -644,7 +646,7 @@ get_keybinding_text(struct listbox_item *item, struct terminal *term)
return stracpy(keybinding_text_toggle ? keymap->str
: _(keymap->desc, term));
} else if (item->depth < 2) {
struct action *action = item->udata;
const struct action *action = item->udata;
return stracpy(keybinding_text_toggle ? action->str
: _(action->desc, term));
@ -686,7 +688,7 @@ get_keybinding_root(struct listbox_item *item)
if (item->depth == 0) return NULL;
if (item->depth == 1) {
struct action *action = item->udata;
const struct action *action = item->udata;
return keymap_box_item[action->keymap_id];
} else {
@ -700,7 +702,7 @@ static enum listbox_match
match_keybinding(struct listbox_item *item, struct terminal *term,
unsigned char *text)
{
struct action *action = item->udata;
const struct action *action = item->udata;
unsigned char *desc;
if (item->depth != 1)
@ -872,7 +874,7 @@ push_kbdbind_add_button(struct dialog_data *dlg_data,
hop->action_id = keybinding->action_id;
hop->keymap_id = keybinding->keymap_id;
} else {
struct action *action = item->udata;
const struct action *action = item->udata;
hop->action_id = action->num;
hop->keymap_id = action->keymap_id;

View File

@ -16,7 +16,8 @@ void options_manager(struct session *);
void keybinding_manager(struct session *);
struct listbox_item *get_keybinding_action_box_item(enum keymap_id keymap_id, action_id_T action_id);
void init_keybinding_listboxes(struct keymap keymap_table[], struct action_list actions[]);
void init_keybinding_listboxes(struct keymap keymap_table[],
const struct action_list actions[]);
void done_keybinding_listboxes(void);
#endif

View File

@ -26,7 +26,7 @@
/* Fix namespace clash on MacOS. */
#define table table_elinks
static struct action_list action_table[KEYMAP_MAX];
static const struct action_list action_table[KEYMAP_MAX];
static struct keymap keymap_table[KEYMAP_MAX];
static LIST_OF(struct keybinding) keymaps[KEYMAP_MAX];
@ -229,7 +229,7 @@ static struct keymap keymap_table[] = {
* Config file helpers.
*/
static struct action *
static const struct action *
get_action_from_keystroke(enum keymap_id keymap_id,
const unsigned char *keystroke_str)
{
@ -243,8 +243,8 @@ unsigned char *
get_action_name_from_keystroke(enum keymap_id keymap_id,
const unsigned char *keystroke_str)
{
struct action *action = get_action_from_keystroke(keymap_id,
keystroke_str);
const struct action *action = get_action_from_keystroke(keymap_id,
keystroke_str);
return action ? action->str : NULL;
}
@ -252,7 +252,7 @@ get_action_name_from_keystroke(enum keymap_id keymap_id,
action_id_T
get_action_from_string(enum keymap_id keymap_id, unsigned char *str)
{
struct action *action;
const struct action *action;
assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
@ -263,7 +263,7 @@ get_action_from_string(enum keymap_id keymap_id, unsigned char *str)
return -1;
}
struct action *
const struct action *
get_action(enum keymap_id keymap_id, action_id_T action_id)
{
assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
@ -277,7 +277,7 @@ get_action(enum keymap_id keymap_id, action_id_T action_id)
unsigned char *
get_action_name(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action ? action->str : NULL;
}
@ -285,7 +285,7 @@ get_action_name(enum keymap_id keymap_id, action_id_T action_id)
static unsigned char *
get_action_desc(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action ? (action->desc ? action->desc : action->str)
: NULL;
@ -528,23 +528,23 @@ add_actions_to_string(struct string *string, action_id_T action_ids[],
#undef KEYMAP_ID
#define KEYMAP_ID KEYMAP_MAIN
static struct action main_action_table[MAIN_ACTIONS + 1] = {
static const struct action main_action_table[MAIN_ACTIONS + 1] = {
#include "config/actions-main.inc"
};
#undef KEYMAP_ID
#define KEYMAP_ID KEYMAP_EDIT
static struct action edit_action_table[EDIT_ACTIONS + 1] = {
static const struct action edit_action_table[EDIT_ACTIONS + 1] = {
#include "config/actions-edit.inc"
};
#undef KEYMAP_ID
#define KEYMAP_ID KEYMAP_MENU
static struct action menu_action_table[MENU_ACTIONS + 1] = {
static const struct action menu_action_table[MENU_ACTIONS + 1] = {
#include "config/actions-menu.inc"
};
static struct action_list action_table[KEYMAP_MAX] = {
static const struct action_list action_table[KEYMAP_MAX] = {
{ main_action_table, sizeof_array(main_action_table) },
{ edit_action_table, sizeof_array(edit_action_table) },
{ menu_action_table, sizeof_array(menu_action_table) },
@ -849,11 +849,11 @@ add_default_keybindings(void)
*/
struct action_alias {
unsigned char *str;
const unsigned char *str;
action_id_T action_id;
};
static struct action_alias main_action_aliases[] = {
static const struct action_alias main_action_aliases[] = {
{ "back", ACT_MAIN_HISTORY_MOVE_BACK },
{ "down", ACT_MAIN_MOVE_LINK_NEXT },
{ "download", ACT_MAIN_LINK_DOWNLOAD },
@ -874,13 +874,13 @@ static struct action_alias main_action_aliases[] = {
{ NULL, 0 }
};
static struct action_alias edit_action_aliases[] = {
static const struct action_alias edit_action_aliases[] = {
{ "edit", ACT_EDIT_OPEN_EXTERNAL },
{ NULL, 0 }
};
static struct action_alias *action_aliases[KEYMAP_MAX] = {
static const struct action_alias *action_aliases[KEYMAP_MAX] = {
main_action_aliases,
edit_action_aliases,
NULL,
@ -892,7 +892,7 @@ get_aliased_action(enum keymap_id keymap_id, unsigned char *action_str)
assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
if (action_aliases[keymap_id]) {
struct action_alias *alias;
const struct action_alias *alias;
for (alias = action_aliases[keymap_id]; alias->str; alias++)
if (!strcmp(alias->str, action_str))

View File

@ -30,7 +30,7 @@ struct action {
};
struct action_list {
struct action *actions;
const struct action *actions;
int num_actions;
};
struct keymap {
@ -53,42 +53,26 @@ enum action_flags {
* and also update the table action_table[] in kbdbind.c. */
#define ACTION_(map, name, action, caption, flags) \
ACT_##map##_OFFSET_##action
ACT_##map##_##action
enum main_action_offset {
enum main_action {
#include "config/actions-main.inc"
MAIN_ACTIONS,
};
enum edit_action_offset {
enum edit_action {
#include "config/actions-edit.inc"
EDIT_ACTIONS
};
enum menu_action_offset {
enum menu_action {
#include "config/actions-menu.inc"
MENU_ACTIONS
};
#undef ACTION_
#define ACTION_(map, name, action, caption, flags) \
ACT_##map##_##action
enum main_action {
#include "config/actions-main.inc"
};
enum edit_action {
#include "config/actions-edit.inc"
};
enum menu_action {
#include "config/actions-menu.inc"
};
#undef ACTION_
enum kbdbind_flags {
@ -120,7 +104,7 @@ struct keybinding *add_keybinding(enum keymap_id keymap_id, action_id_T action_i
int keybinding_exists(enum keymap_id keymap_id, struct term_event_keyboard *kbd, action_id_T *action_id);
void free_keybinding(struct keybinding *);
struct action *get_action(enum keymap_id keymap_id, action_id_T action_id);
const struct action *get_action(enum keymap_id keymap_id, action_id_T action_id);
unsigned char *get_action_name(enum keymap_id keymap_id, action_id_T action_id);
action_id_T get_action_from_string(enum keymap_id keymap_id, unsigned char *str);
unsigned char *get_action_name_from_keystroke(enum keymap_id keymap_id,
@ -129,7 +113,7 @@ unsigned char *get_action_name_from_keystroke(enum keymap_id keymap_id,
static inline unsigned int
action_is_anonymous_safe(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action && !(action->flags & ACTION_RESTRICT_ANONYMOUS);
}
@ -137,7 +121,7 @@ action_is_anonymous_safe(enum keymap_id keymap_id, action_id_T action_id)
static inline unsigned int
action_requires_view_state(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action && (action->flags & ACTION_REQUIRE_VIEW_STATE);
}
@ -145,7 +129,7 @@ action_requires_view_state(enum keymap_id keymap_id, action_id_T action_id)
static inline unsigned int
action_requires_location(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action && (action->flags & ACTION_REQUIRE_LOCATION);
}
@ -153,7 +137,7 @@ action_requires_location(enum keymap_id keymap_id, action_id_T action_id)
static inline unsigned int
action_prefix_is_link_number(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action && (action->flags & ACTION_JUMP_TO_LINK);
}
@ -161,7 +145,7 @@ action_prefix_is_link_number(enum keymap_id keymap_id, action_id_T action_id)
static inline unsigned int
action_requires_link(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action && (action->flags & ACTION_REQUIRE_LINK);
}
@ -169,7 +153,7 @@ action_requires_link(enum keymap_id keymap_id, action_id_T action_id)
static inline unsigned int
action_requires_form(enum keymap_id keymap_id, action_id_T action_id)
{
struct action *action = get_action(keymap_id, action_id);
const struct action *action = get_action(keymap_id, action_id);
return action && (action->flags & ACTION_REQUIRE_FORM);
}

View File

@ -117,7 +117,7 @@ struct connection_state {
* structure holds a system error instead. */
enum connection_basic_state basic;
/** When #state is ::S_ERRNO, syserr is the saved value of
/** When #basic is ::S_ERRNO, syserr is the saved value of
* errno. Otherwise, syserr should be 0. */
int syserr;
};

View File

@ -32,7 +32,7 @@ struct terminal;
#define BITTORRENT_REQUEST_LENGTH (1 << 14)
/** The length of requested blocks of pieces should not exceed 2^17 bytes.
* Used for the protocol.bittorrent.max_request_length option
* Used for the protocol.bittorrent.max_request_length option.
* Bram uses 2^23 here. */
#define BITTORRENT_REQUEST_ACCEPT_LENGTH (1 << 23)

View File

@ -616,8 +616,11 @@ abort_bittorrent_download_query(struct dialog_data *dlg_data)
done_bittorrent_download_info(info);
}
/* The download button handler. Basicly it redirects <uri> to bittorrent:<uri>
* and starts displaying the download. */
/** The download button handler. Basicly it redirects <uri> to bittorrent:<uri>
* and starts displaying the download.
*
* bittorrent_query_callback() passes this function as a
* ::widget_handler_T to add_dlg_button(). */
static widget_handler_status_T
bittorrent_download(struct dialog_data *dlg_data, struct widget_data *widget_data)
{
@ -675,7 +678,7 @@ bittorrent_download(struct dialog_data *dlg_data, struct widget_data *widget_dat
/* Show the protocol header. */
/* XXX: Code duplication with session/download.h */
widget_handler_status_T
static widget_handler_status_T
tp_show_header(struct dialog_data *dlg_data, struct widget_data *widget_data)
{
struct type_query *type_query = widget_data->widget->data;
@ -685,7 +688,10 @@ tp_show_header(struct dialog_data *dlg_data, struct widget_data *widget_data)
return EVENT_PROCESSED;
}
/* Build a dialog querying the user on how to handle a .torrent file. */
/** Build a dialog querying the user on how to handle a .torrent file.
*
* query_bittorrent_dialog() passes this function as a
* ::bittorrent_fetch_callback_T to init_bittorrent_fetch(). */
static void
bittorrent_query_callback(void *data, struct connection_state state,
struct bittorrent_const_string *response)

View File

@ -251,7 +251,7 @@ utf8_to_jsstring(JSContext *ctx, const unsigned char *str, int length)
* @param[in] utf16
* Pointer to the first element in an array of jschars.
*
* @param[i] len
* @param[in] len
* Number of jschars in the @a utf16 array.
*
* @return @a utf8 if successful, or NULL if not. */

View File

@ -87,6 +87,8 @@ are_there_downloads(void)
static void download_data(struct download *download, struct file_download *file_download);
/*! @note If this fails, the caller is responsible of freeing @a file
* and closing @a fd. */
struct file_download *
init_file_download(struct uri *uri, struct session *ses, unsigned char *file, int fd)
{
@ -458,126 +460,235 @@ download_data(struct download *download, struct file_download *file_download)
download_data_store(download, file_download);
}
/** Type of the callback function that will be called when the user
* answers the question posed by lookup_unique_name().
*
* @param term
* The terminal on which the callback should display any windows.
* Comes directly from the @a term argument of lookup_unique_name().
*
* @param file
* The name of the local file to which the data should be downloaded,
* or NULL if the download should not begin. The callback is
* responsible of doing mem_free(@a file).
*
* @param data
* A pointer to any data that the callback cares about.
* Comes directly from the @a data argument of lookup_unique_name().
*
* @param flags
* The same as the @a flags argument of create_download_file(),
* except the ::DOWNLOAD_RESUME_SELECTED bit will be changed to match
* what the user chose.
*
* @relates lun_hop */
typedef void lun_callback_T(struct terminal *term, unsigned char *file,
void *data, enum download_flags flags);
/* XXX: We assume that resume is everytime zero in lun's callbacks. */
/** The user is being asked what to do when the local file for
* the download already exists. This structure is allocated by
* lookup_unique_name() and freed by each lun_* function:
* lun_alternate(), lun_cancel(), lun_overwrite(), and lun_resume(). */
struct lun_hop {
/** The terminal in which ELinks is asking the question.
* This gets passed to #callback. */
struct terminal *term;
unsigned char *ofile, *file;
void (*callback)(struct terminal *, unsigned char *, void *, download_flags_T);
/** The name of the local file into which the data was
* originally going to be downloaded, but which already
* exists. In this string, "~" has already been expanded
* to the home directory. The string must be freed with
* mem_free(). */
unsigned char *ofile;
/** An alternative file name that the user may choose instead
* of #ofile. The string must be freed with mem_free(). */
unsigned char *file;
/** This function will be called when the user answers. */
lun_callback_T *callback;
/** A pointer to be passed to #callback. */
void *data;
/** Saved flags to be passed to #callback.
* If the user chooses to resume, then lun_resume() sets
* ::DOWNLOAD_RESUME_SELECTED when it calls #callback.
*
* @invariant The ::DOWNLOAD_RESUME_SELECTED bit should be
* clear here because otherwise there would have been no
* reason to ask the user and initialize this structure. */
enum download_flags flags;
};
enum {
COMMON_DOWNLOAD_DO = 0,
CONTINUE_DOWNLOAD_DO
};
/** Data saved by common_download() for the common_download_do()
* callback. */
struct cmdw_hop {
int magic; /* Must be first --witekfl */
struct session *ses;
/** The URI from which the data will be downloaded. */
struct uri *download_uri;
/** The name of the local file to which the data will be
* downloaded. This is initially NULL, but its address is
* given to create_download_file(), which arranges for the
* pointer to be set before common_download_do() is called.
* The string must be freed with mem_free(). */
unsigned char *real_file;
};
/** Data saved by continue_download() for the continue_download_do()
* callback. */
struct codw_hop {
int magic; /* must be first --witekfl */
struct type_query *type_query;
/** The name of the local file to which the data will be
* downloaded. This is initially NULL, but its address is
* given to create_download_file(), which arranges for the
* pointer to be set before continue_download_do() is called.
* The string must be freed with mem_free(). */
unsigned char *real_file;
unsigned char *file;
};
/** Data saved by create_download_file() for the create_download_file_do()
* callback. */
struct cdf_hop {
/** Where to save the name of the file that was actually
* opened. One of the arguments of #callback is a file
* descriptor for this file. @c real_file can be NULL if
* #callback does not care about the name. */
unsigned char **real_file;
void (*callback)(struct terminal *, int, void *, download_flags_T);
/** This function will be called when the file has been opened,
* or when it is known that the file will not be opened. */
cdf_callback_T *callback;
/** A pointer to be passed to #callback. */
void *data;
};
/** The use chose "Save under the alternative name" when asked where
* to download a file.
*
* lookup_unique_name() passes this function as a ::done_handler_T to
* msg_box().
*
* @relates lun_hop */
static void
lun_alternate(void *lun_hop_)
{
struct lun_hop *lun_hop = lun_hop_;
lun_hop->callback(lun_hop->term, lun_hop->file, lun_hop->data, DOWNLOAD_START);
lun_hop->callback(lun_hop->term, lun_hop->file, lun_hop->data,
lun_hop->flags);
mem_free_if(lun_hop->ofile);
mem_free(lun_hop);
}
/** The use chose "Cancel" when asked where to download a file.
*
* lookup_unique_name() passes this function as a ::done_handler_T to
* msg_box().
*
* @relates lun_hop */
static void
lun_cancel(void *lun_hop_)
{
struct lun_hop *lun_hop = lun_hop_;
lun_hop->callback(lun_hop->term, NULL, lun_hop->data, DOWNLOAD_START);
lun_hop->callback(lun_hop->term, NULL, lun_hop->data,
lun_hop->flags);
mem_free_if(lun_hop->ofile);
mem_free_if(lun_hop->file);
mem_free(lun_hop);
}
/** The use chose "Overwrite the original file" when asked where to
* download a file.
*
* lookup_unique_name() passes this function as a ::done_handler_T to
* msg_box().
*
* @relates lun_hop */
static void
lun_overwrite(void *lun_hop_)
{
struct lun_hop *lun_hop = lun_hop_;
lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data, 0);
lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data,
lun_hop->flags);
mem_free_if(lun_hop->file);
mem_free(lun_hop);
}
static void common_download_do(struct terminal *term, int fd, void *data, download_flags_T flags);
/** The user chose "Resume download of the original file" when asked
* where to download a file.
*
* lookup_unique_name() passes this function as a ::done_handler_T to
* msg_box().
*
* @relates lun_hop */
static void
lun_resume(void *lun_hop_)
{
struct lun_hop *lun_hop = lun_hop_;
struct cdf_hop *cdf_hop = lun_hop->data;
int magic = *(int *)cdf_hop->data;
if (magic == CONTINUE_DOWNLOAD_DO) {
struct cmdw_hop *cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop));
if (!cmdw_hop) {
lun_cancel(lun_hop);
return;
} else {
struct codw_hop *codw_hop = cdf_hop->data;
struct type_query *type_query = codw_hop->type_query;
cmdw_hop->magic = COMMON_DOWNLOAD_DO;
cmdw_hop->ses = type_query->ses;
/* FIXME: Current ses->download_uri is overwritten here --witekfl */
cmdw_hop->ses->download_uri = get_uri_reference(type_query->uri);
if (type_query->external_handler) mem_free_if(codw_hop->file);
tp_cancel(type_query);
mem_free(codw_hop);
cdf_hop->real_file = &cmdw_hop->real_file;
cdf_hop->data = cmdw_hop;
cdf_hop->callback = common_download_do;
}
}
lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data, DOWNLOAD_RESUME);
lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data,
lun_hop->flags | DOWNLOAD_RESUME_SELECTED);
mem_free_if(lun_hop->file);
mem_free(lun_hop);
}
/** If attempting to download to an existing file, perhaps ask
* the user whether to resume, overwrite, or save elsewhere.
* This function constructs a struct lun_hop, which will be freed
* when the user answers the question.
*
* @param term
* The terminal in which this function should show its UI.
*
* @param[in] ofile
* A proposed name for the local file to which the data would be
* downloaded. "~" here refers to the home directory.
* lookup_unique_name() treats this original string as read-only.
*
* @param[in] flags
* Flags controlling how to download the file.
* ::DOWNLOAD_RESUME_ALLOWED adds a "Resume" button to the dialog.
* ::DOWNLOAD_RESUME_SELECTED means the user already chose to resume
* downloading (with ::ACT_MAIN_LINK_DOWNLOAD_RESUME), before ELinks
* even asked for the file name; thus don't ask whether to overwrite.
* Other flags, such as ::DOWNLOAD_EXTERNAL, have no effect at this
* level but they get passed to @a callback.
*
* @param callback
* Will be called when the user answers, or right away if the question
* need not or cannot be asked.
*
* @param data
* A pointer to be passed to @a callback.
*
* @relates lun_hop */
static void
lookup_unique_name(struct terminal *term, unsigned char *ofile, download_flags_T flags,
void (*callback)(struct terminal *, unsigned char *, void *, download_flags_T flags),
void *data)
lookup_unique_name(struct terminal *term, unsigned char *ofile,
enum download_flags flags,
lun_callback_T *callback, void *data)
{
/* [gettext_accelerator_context(.lookup_unique_name)] */
struct lun_hop *lun_hop;
unsigned char *file;
struct lun_hop *lun_hop = NULL;
unsigned char *file = NULL;
struct dialog_data *dialog_data;
int overwrite;
ofile = expand_tilde(ofile);
if (!ofile) goto error;
/* Minor code duplication to prevent useless call to get_opt_int()
* if possible. --Zas */
if (flags & DOWNLOAD_RESUME) {
if (flags & DOWNLOAD_RESUME_SELECTED) {
callback(term, ofile, data, flags);
return;
}
@ -598,9 +709,7 @@ lookup_unique_name(struct terminal *term, unsigned char *ofile, download_flags_T
N_("Download error"), ALIGN_CENTER,
msg_text(term, N_("'%s' is a directory."),
ofile));
mem_free(ofile);
callback(term, NULL, data, flags);
return;
goto error;
}
/* Check if the file already exists (file != ofile). */
@ -609,7 +718,7 @@ lookup_unique_name(struct terminal *term, unsigned char *ofile, download_flags_T
if (!file || overwrite == 1 || file == ofile) {
/* Still nothing special to do... */
if (file != ofile) mem_free(ofile);
callback(term, file, data, flags);
callback(term, file, data, flags & ~DOWNLOAD_RESUME_SELECTED);
return;
}
@ -617,19 +726,16 @@ lookup_unique_name(struct terminal *term, unsigned char *ofile, download_flags_T
* exists) */
lun_hop = mem_calloc(1, sizeof(*lun_hop));
if (!lun_hop) {
if (file != ofile) mem_free(file);
mem_free(ofile);
callback(term, NULL, data, flags);
return;
}
if (!lun_hop) goto error;
lun_hop->term = term;
lun_hop->ofile = ofile;
lun_hop->file = (file != ofile) ? file : stracpy(ofile);
lun_hop->file = file; /* file != ofile verified above */
lun_hop->callback = callback;
lun_hop->data = data;
lun_hop->flags = flags;
msg_box(term, NULL, MSGBOX_FREE_TEXT,
dialog_data = msg_box(
term, NULL, MSGBOX_FREE_TEXT,
N_("File exists"), ALIGN_CENTER,
msg_text(term, N_("This file already exists:\n"
"%s\n\n"
@ -640,15 +746,34 @@ lookup_unique_name(struct terminal *term, unsigned char *ofile, download_flags_T
lun_hop, 4,
MSG_BOX_BUTTON(N_("Sa~ve under the alternative name"), lun_alternate, B_ENTER),
MSG_BOX_BUTTON(N_("~Overwrite the original file"), lun_overwrite, 0),
MSG_BOX_BUTTON(N_("~Resume download of the original file"), lun_resume, 0),
MSG_BOX_BUTTON((flags & DOWNLOAD_RESUME_ALLOWED
? N_("~Resume download of the original file")
: NULL),
lun_resume, 0),
MSG_BOX_BUTTON(N_("~Cancel"), lun_cancel, B_ESC));
if (!dialog_data) goto error;
return;
error:
mem_free_if(lun_hop);
if (file != ofile) mem_free_if(file);
mem_free_if(ofile);
callback(term, NULL, data, flags & ~DOWNLOAD_RESUME_SELECTED);
}
/** Now that the final name of the download file has been chosen,
* open the file and call the ::cdf_callback_T that was originally
* given to create_download_file().
*
* create_download_file() passes this function as a ::lun_callback_T
* to lookup_unique_name().
*
* @relates cdf_hop */
static void
create_download_file_do(struct terminal *term, unsigned char *file, void *data,
download_flags_T flags)
create_download_file_do(struct terminal *term, unsigned char *file,
void *data, enum download_flags flags)
{
struct cdf_hop *cdf_hop = data;
unsigned char *wd;
@ -657,7 +782,7 @@ create_download_file_do(struct terminal *term, unsigned char *file, void *data,
#ifdef NO_FILE_SECURITY
int sf = 0;
#else
int sf = flags & DOWNLOAD_EXTERNAL;
int sf = !!(flags & DOWNLOAD_EXTERNAL);
#endif
if (!file) goto finish;
@ -671,8 +796,9 @@ create_download_file_do(struct terminal *term, unsigned char *file, void *data,
/* O_APPEND means repositioning at the end of file before each write(),
* thus ignoring seek()s and that can hide mysterious bugs. IMHO.
* --pasky */
h = open(file, O_CREAT | O_WRONLY | (flags & DOWNLOAD_RESUME ? 0 : O_TRUNC)
| (sf && !(flags & DOWNLOAD_RESUME) ? O_EXCL : 0),
h = open(file, O_CREAT | O_WRONLY
| (flags & DOWNLOAD_RESUME_SELECTED ? 0 : O_TRUNC)
| (sf && !(flags & DOWNLOAD_RESUME_SELECTED) ? O_EXCL : 0),
sf ? 0600 : 0666);
saved_errno = errno; /* Saved in case of ... --Zas */
@ -717,17 +843,51 @@ finish:
mem_free(cdf_hop);
}
/** Create a file to which data can be downloaded.
* This function constructs a struct cdf_hop that will be freed
* when @a callback returns.
*
* @param term
* If any dialog boxes are needed, show them in this terminal.
*
* @param fi
* A proposed name for the local file to which the data would be
* downloaded. "~" here refers to the home directory.
* create_download_file() treats this original string as read-only.
*
* @param real_file
* If non-NULL, prepare to save in *@a real_file the name of the local
* file that was eventually opened. @a callback must then arrange for
* this string to be freed with mem_free().
*
* @param flags
* Flags controlling how to download the file.
* ::DOWNLOAD_RESUME_ALLOWED adds a "Resume" button to the dialog.
* ::DOWNLOAD_RESUME_SELECTED skips the dialog entirely.
* ::DOWNLOAD_EXTERNAL causes the file to be created with settings
* suitable for a temporary file: give only the user herself access to
* the file (even if the umask is looser), and create the file with
* @c O_EXCL unless resuming.
*
* @param callback
* This function will be called when the file has been opened,
* or when it is known that the file will not be opened.
*
* @param data
* A pointer to be passed to @a callback.
*
* @relates cdf_hop */
void
create_download_file(struct terminal *term, unsigned char *fi,
unsigned char **real_file, download_flags_T flags,
void (*callback)(struct terminal *, int, void *, download_flags_T),
void *data)
unsigned char **real_file,
enum download_flags flags,
cdf_callback_T *callback, void *data)
{
struct cdf_hop *cdf_hop = mem_calloc(1, sizeof(*cdf_hop));
unsigned char *wd;
if (!cdf_hop) {
callback(term, -1, data, 0);
callback(term, -1, data, flags & ~DOWNLOAD_RESUME_SELECTED);
return;
}
@ -833,32 +993,56 @@ subst_file(unsigned char *prog, unsigned char *file)
/*! common_download() passes this function as a ::cdf_callback_T to
* create_download_file().
*
* @relates cmdw_hop */
static void
common_download_do(struct terminal *term, int fd, void *data, download_flags_T flags)
common_download_do(struct terminal *term, int fd, void *data,
enum download_flags flags)
{
struct file_download *file_download;
struct cmdw_hop *cmdw_hop = data;
struct uri *download_uri = cmdw_hop->download_uri;
unsigned char *file = cmdw_hop->real_file;
struct session *ses = cmdw_hop->ses;
struct stat buf;
mem_free(cmdw_hop);
if (!file || fstat(fd, &buf)) return;
if (!file || fstat(fd, &buf)) goto finish;
file_download = init_file_download(ses->download_uri, ses, file, fd);
if (!file_download) return;
file_download = init_file_download(download_uri, ses, file, fd);
if (!file_download) goto finish;
/* If init_file_download succeeds, it takes ownership of file
* and fd. */
file = NULL;
fd = -1;
if (flags & DOWNLOAD_RESUME) file_download->seek = buf.st_size;
if (flags & DOWNLOAD_RESUME_SELECTED)
file_download->seek = buf.st_size;
display_download(ses->tab->term, file_download, ses);
load_uri(file_download->uri, ses->referrer, &file_download->download,
PRI_DOWNLOAD, CACHE_MODE_NORMAL, file_download->seek);
finish:
mem_free_if(file);
if (fd != -1) close(fd);
done_uri(download_uri);
}
/** Begin or resume downloading from session.download_uri to the
* @a file specified by the user.
*
* This function contains the code shared between start_download() and
* resume_download().
*
* @relates cmdw_hop */
static void
common_download(struct session *ses, unsigned char *file, download_flags_T flags)
common_download(struct session *ses, unsigned char *file,
enum download_flags flags)
{
struct cmdw_hop *cmdw_hop;
@ -867,30 +1051,81 @@ common_download(struct session *ses, unsigned char *file, download_flags_T flags
cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop));
if (!cmdw_hop) return;
cmdw_hop->ses = ses;
cmdw_hop->magic = COMMON_DOWNLOAD_DO;
cmdw_hop->download_uri = ses->download_uri;
ses->download_uri = NULL;
kill_downloads_to_file(file);
create_download_file(ses->tab->term, file, &cmdw_hop->real_file, flags,
common_download_do, cmdw_hop);
create_download_file(ses->tab->term, file, &cmdw_hop->real_file,
flags, common_download_do, cmdw_hop);
}
/** Begin downloading from session.download_uri to the @a file
* specified by the user.
*
* The ::ACT_MAIN_SAVE_AS, ::ACT_MAIN_SAVE_URL_AS,
* ::ACT_MAIN_LINK_DOWNLOAD, and ::ACT_MAIN_LINK_DOWNLOAD_IMAGE
* actions pass this function as the @c std callback to query_file().
*
* @relates cmdw_hop */
void
start_download(void *ses, unsigned char *file)
{
common_download(ses, file, DOWNLOAD_START);
common_download(ses, file,
DOWNLOAD_RESUME_ALLOWED);
}
/** Resume downloading from session.download_uri to the @a file
* specified by the user.
*
* The ::ACT_MAIN_LINK_DOWNLOAD_RESUME action passes this function as
* the @c std callback to query_file().
*
* @relates cmdw_hop */
void
resume_download(void *ses, unsigned char *file)
{
common_download(ses, file, DOWNLOAD_RESUME);
common_download(ses, file,
DOWNLOAD_RESUME_ALLOWED | DOWNLOAD_RESUME_SELECTED);
}
/** Resume downloading a file, based on information in struct
* codw_hop. This function actually starts a new download from the
* current end of the file, even though a download from the beginning
* is already in progress at codw_hop->type_query->download. The
* caller will cancel the preexisting download after this function
* returns.
*
* @relates codw_hop */
static void
continue_download_do(struct terminal *term, int fd, void *data, download_flags_T flags)
transform_codw_to_cmdw(struct terminal *term, int fd,
struct codw_hop *codw_hop,
enum download_flags flags)
{
struct type_query *type_query = codw_hop->type_query;
struct cmdw_hop *cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop));
if (!cmdw_hop) {
close(fd);
return;
}
cmdw_hop->ses = type_query->ses;
cmdw_hop->download_uri = get_uri_reference(type_query->uri);
cmdw_hop->real_file = codw_hop->real_file;
codw_hop->real_file = NULL;
common_download_do(term, fd, cmdw_hop, flags);
}
/*! continue_download() passes this function as a ::cdf_callback_T to
* create_download_file().
*
* @relates codw_hop */
static void
continue_download_do(struct terminal *term, int fd, void *data,
enum download_flags flags)
{
struct codw_hop *codw_hop = data;
struct file_download *file_download = NULL;
@ -904,9 +1139,19 @@ continue_download_do(struct terminal *term, int fd, void *data, download_flags_T
type_query = codw_hop->type_query;
if (!codw_hop->real_file) goto cancel;
if (flags & DOWNLOAD_RESUME_SELECTED) {
transform_codw_to_cmdw(term, fd, codw_hop, flags);
fd = -1; /* ownership transfer */
goto cancel;
}
file_download = init_file_download(type_query->uri, type_query->ses,
codw_hop->real_file, fd);
if (!file_download) goto cancel;
/* If init_file_download succeeds, it takes ownership of
* codw_hop->real_file and fd. */
codw_hop->real_file = NULL;
fd = -1;
if (type_query->external_handler) {
file_download->external_handler = subst_file(type_query->external_handler,
@ -929,11 +1174,22 @@ continue_download_do(struct terminal *term, int fd, void *data, download_flags_T
return;
cancel:
mem_free_if(codw_hop->real_file);
if (fd != -1) close(fd);
if (type_query->external_handler) mem_free_if(codw_hop->file);
tp_cancel(type_query);
mem_free(codw_hop);
}
/** When asked what to do with a file, the user chose to download it
* to a local file named @a file.
* Or an external handler was selected, in which case
* type_query.external_handler is non-NULL and @a file does not
* matter because this function will generate a name.
*
* tp_save() passes this function as the @c std callback to query_file().
*
* @relates codw_hop */
static void
continue_download(void *data, unsigned char *file)
{
@ -957,19 +1213,19 @@ continue_download(void *data, unsigned char *file)
codw_hop->type_query = type_query;
codw_hop->file = file;
codw_hop->magic = CONTINUE_DOWNLOAD_DO;
kill_downloads_to_file(file);
create_download_file(type_query->ses->tab->term, file,
&codw_hop->real_file,
type_query->external_handler
? DOWNLOAD_START | DOWNLOAD_EXTERNAL
: DOWNLOAD_START,
? DOWNLOAD_RESUME_ALLOWED | DOWNLOAD_EXTERNAL
: DOWNLOAD_RESUME_ALLOWED,
continue_download_do, codw_hop);
}
/*! @relates type_query */
static struct type_query *
find_type_query(struct session *ses)
{
@ -982,6 +1238,11 @@ find_type_query(struct session *ses)
return NULL;
}
/** Prepare to ask the user what to do with a file, but don't display
* the window yet. To display it, do_type_query() must be called
* separately. setup_download_handler() takes care of that.
*
* @relates type_query */
static struct type_query *
init_type_query(struct session *ses, struct download *download,
struct cache_entry *cached)
@ -1007,6 +1268,10 @@ init_type_query(struct session *ses, struct download *download,
return type_query;
}
/** Cancel any download started for @a type_query, remove the structure
* from the session.type_queries list, and free it.
*
* @relates type_query */
void
done_type_query(struct type_query *type_query)
{
@ -1022,6 +1287,14 @@ done_type_query(struct type_query *type_query)
}
/** The user chose "Cancel" when asked what to do with a file,
* or the type query was cancelled for some other reason.
*
* do_type_query() and bittorrent_query_callback() pass this function
* as a ::done_handler_T to add_dlg_ok_button(), and tp_save() passes
* this function as a @c cancel callback to query_file().
*
* @relates type_query */
void
tp_cancel(void *data)
{
@ -1033,6 +1306,13 @@ tp_cancel(void *data)
}
/** The user chose "Save" when asked what to do with a file.
* Now ask her where to save the file.
*
* do_type_query() and bittorrent_query_callback() pass this function
* as a ::done_handler_T to add_dlg_ok_button().
*
* @relates type_query */
void
tp_save(struct type_query *type_query)
{
@ -1040,8 +1320,14 @@ tp_save(struct type_query *type_query)
query_file(type_query->ses, type_query->uri, type_query, continue_download, tp_cancel, 1);
}
/** This button handler uses the add_dlg_button() interface so that pressing
* 'Show header' will not close the type query dialog. */
/** The user chose "Show header" when asked what to do with a file.
*
* do_type_query() passes this function as a ::widget_handler_T to
* add_dlg_button(). Unlike with add_dlg_ok_button(), pressing this
* button does not close the dialog box. This way, the user can
* first examine the header and then choose what to do.
*
* @relates type_query */
static widget_handler_status_T
tp_show_header(struct dialog_data *dlg_data, struct widget_data *widget_data)
{
@ -1053,10 +1339,18 @@ tp_show_header(struct dialog_data *dlg_data, struct widget_data *widget_data)
}
/** @bug FIXME: We need to modify this function to take frame data
/** The user chose "Display" when asked what to do with a file,
* or she chose "Open" and there is no external handler.
*
* do_type_query() and bittorrent_query_callback() pass this function
* as a ::done_handler_T to add_dlg_ok_button().
*
* @bug FIXME: We need to modify this function to take frame data
* instead, as we want to use this function for frames as well (now,
* when frame has content type text/plain, it is ignored and displayed
* as HTML). */
* as HTML).
*
* @relates type_query */
void
tp_display(struct type_query *type_query)
{
@ -1086,6 +1380,14 @@ tp_display(struct type_query *type_query)
done_type_query(type_query);
}
/** The user chose "Open" when asked what to do with a file.
* Or an external handler was found and it has been configured
* to run without asking.
*
* do_type_query() passes this function as a ::done_handler_T to
* add_dlg_ok_button().
*
* @relates type_query */
static void
tp_open(struct type_query *type_query)
{
@ -1119,6 +1421,13 @@ tp_open(struct type_query *type_query)
}
/*! Ask the user what to do with a file.
*
* This function does not support BitTorrent downloads.
* For those, query_bittorrent_dialog() must be called instead.
* setup_download_handler() takes care of this.
*
* @relates type_query */
static void
do_type_query(struct type_query *type_query, unsigned char *ct, struct mime_handler *handler)
{
@ -1211,7 +1520,7 @@ do_type_query(struct type_query *type_query, unsigned char *ct, struct mime_hand
0, 0, NULL, MAX_STR_LEN, field, NULL);
type_query->external_handler = field;
add_dlg_radio(dlg, _("Block the terminal", term), 0, 0, &type_query->block);
add_dlg_checkbox(dlg, _("Block the terminal", term), &type_query->block);
selected_widget = 3;
} else if (handler) {
@ -1300,6 +1609,7 @@ struct {
{ NULL, 1 },
};
/*! @relates type_query */
int
setup_download_handler(struct session *ses, struct download *loading,
struct cache_entry *cached, int frame)

View File

@ -20,11 +20,22 @@ struct download;
typedef void (download_callback_T)(struct download *, void *);
typedef uint32_t download_flags_T;
enum download_flag {
DOWNLOAD_START = 0,
DOWNLOAD_RESUME = 1,
DOWNLOAD_EXTERNAL = 2,
/** Flags controlling how to download a file. This is a bit mask.
* Unrecognized bits should be preserved and ignored. */
enum download_flags {
/** Downloading cannot be resumed; do not offer such an option
* to the user. All bits clear. */
DOWNLOAD_RESUME_DISABLED = 0,
/** Downloading can be resumed. This is the usual value. */
DOWNLOAD_RESUME_ALLOWED = 1,
/** The user wants to resume downloading. This must not occur
* without #DOWNLOAD_RESUME_ALLOWED. */
DOWNLOAD_RESUME_SELECTED = 2,
/** The file will be opened in an external handler. */
DOWNLOAD_EXTERNAL = 4
};
struct download {
@ -46,16 +57,60 @@ struct download {
enum connection_priority pri;
};
/** The user has navigated to a resource that ELinks does not display
* automatically because of its MIME type, and ELinks is asking what
* to do.
*
* These structures are kept in the session.type_queries list, and
* destroy_session() calls done_type_query() to destroy them too. */
struct type_query {
LIST_HEAD(struct type_query);
/** After ELinks has downloaded enough of the resource to see
* that a type query is needed, it moves the download here and
* continues it while the user decides what to do. */
struct download download;
/** Cache entry loaded from #uri. Apparently used only for
* displaying the header. */
struct cache_entry *cached;
/** The session in which the user navigated to #uri. The
* type_query is in the session.type_queries list of this
* session. */
struct session *ses;
/** The URI of the resource about which ELinks is asking.
* This reference must be released with done_uri(). */
struct uri *uri;
/** The name of the frame in which the user navigated to #uri.
* If the user chooses to display the resource, it goes into
* this frame. This string must be freed with mem_free(). */
unsigned char *target_frame;
/** Command line for an external handler, to be run when the
* download finishes. When ELinks displays the type query,
* it copies this from mime_handler.program of the default
* handler of the type. The user can then edit the string.
* This string must be freed with mem_free(). */
unsigned char *external_handler;
/** Whether the external handler is going to use the terminal.
* When ELinks displays the type query, it copies this from
* mime_handler.block of the default handler of the type.
* The user can then change the flag with a checkbox. */
int block;
/** Whether the resource was generated by ELinks running
* a local CGI program. If the user chooses to open the
* resource with an external handler, ELinks normally saves
* the resource to a temporary file and passes the name of
* that to the external handler. However, if the resource is
* from a "file" URI that does not refer to a local CGI, then
* Elinks need not copy the file. */
unsigned int cgi:1;
/* int frame; */
};
@ -116,12 +171,37 @@ int download_is_progressing(struct download *download);
int are_there_downloads(void);
/** Type of the callback function that will be called when the file
* has been opened, or when it is known that the file will not be
* opened.
*
* @param term
* The terminal on which the callback should display any windows.
* Comes directly from the @a term argument of create_download_file().
*
* @param fd
* A file descriptor to the opened file, or -1 if the file will not be
* opened. If the @a real_file argument of create_download_file()
* was not NULL, the callback may read the name of this file from
* *@a real_file.
*
* @param data
* A pointer to any data that the callback cares about.
* Comes directly from the @a data argument of create_download_file().
*
* @param flags
* The same as the @a flags argument of create_download_file(),
* except the ::DOWNLOAD_RESUME_SELECTED bit will be changed to match
* what the user chose.
*
* @relates cdf_hop */
typedef void cdf_callback_T(struct terminal *term, int fd,
void *data, enum download_flags flags);
void start_download(void *, unsigned char *);
void resume_download(void *, unsigned char *);
void create_download_file(struct terminal *, unsigned char *, unsigned char **,
download_flags_T,
void (*)(struct terminal *, int, void *, download_flags_T),
void *);
enum download_flags, cdf_callback_T *, void *);
void abort_all_downloads(void);
void destroy_downloads(struct session *);

View File

@ -164,6 +164,17 @@ struct session {
struct document_view *doc_view;
LIST_OF(struct document_view) scrn_frames;
/** The URI from which the next start_download() or resume_download()
* call should download, or NULL if no such call is pending.
*
* When the user requests a download, one of those functions
* is given as a callback to query_file(), which asks the user
* where to save the downloaded file. The URI cannot be given
* to the callback as a parameter because query_file()
* supports only one void * parameter for the callback and
* that one is already used for the struct session *.
* Instead, the URI is saved here before the query_file()
* call. */
struct uri *download_uri;
/** The URI which is the referrer to the current loaded document

View File

@ -1617,8 +1617,11 @@ save_as(struct session *ses, struct document_view *doc_view, int magic)
return FRAME_EVENT_OK;
}
/*! save_formatted() passes this function as a ::cdf_callback_T to
* create_download_finish(). */
static void
save_formatted_finish(struct terminal *term, int h, void *data, download_flags_T flags)
save_formatted_finish(struct terminal *term, int h,
void *data, enum download_flags flags)
{
struct document *document = data;
@ -1645,7 +1648,8 @@ save_formatted(void *data, unsigned char *file)
assert(doc_view && doc_view->document);
if_assert_failed return;
create_download_file(ses->tab->term, file, NULL, DOWNLOAD_START,
create_download_file(ses->tab->term, file, NULL,
DOWNLOAD_RESUME_DISABLED,
save_formatted_finish, doc_view->document);
}