mirror of
https://github.com/rkd77/elinks.git
synced 2024-09-06 23:44:43 -04: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:
commit
ef548e5728
@ -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
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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. */
|
||||
|
@ -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)
|
||||
|
@ -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 *);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user