netsurf: branch master updated. release/3.10-42-g1f2946a
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/1f2946a7109310ddc8fa2...
...commit http://git.netsurf-browser.org/netsurf.git/commit/1f2946a7109310ddc8fa21f...
...tree http://git.netsurf-browser.org/netsurf.git/tree/1f2946a7109310ddc8fa21ff2...
The branch, master has been updated
via 1f2946a7109310ddc8fa21ff28ce0add14c09a02 (commit)
from 652078f8cf098c1afb6e7c9dfcb0d70415db301c (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=1f2946a7109310ddc8f...
commit 1f2946a7109310ddc8fa21ff28ce0add14c09a02
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
remove unecessary exported function from plain text content handler
diff --git a/content/handlers/text/textplain.c b/content/handlers/text/textplain.c
index 21876ec..60051f5 100644
--- a/content/handlers/text/textplain.c
+++ b/content/handlers/text/textplain.c
@@ -1605,13 +1605,23 @@ textplain_textselection_copy(struct content *c,
return NSERROR_OK;
}
+
+/**
+ * Retrieve the index of the end of the text
+ *
+ * \param[in] c Content to retrieve size of
+ * \return Size, in bytes, of data
+ */
static nserror
textplain_textselection_get_end(struct content *c, unsigned *end_idx)
{
- *end_idx = textplain_size(c);
+ textplain_content *text = (textplain_content *)c;
+
+ *end_idx = text->utf8_data_size;
return NSERROR_OK;
}
+
/**
* plain text content handler table
*/
@@ -1671,12 +1681,3 @@ nserror textplain_init(void)
-/* exported interface documented in html/textplain.h */
-size_t textplain_size(struct content *c)
-{
- textplain_content *text = (textplain_content *) c;
-
- assert(c != NULL);
-
- return text->utf8_data_size;
-}
diff --git a/content/handlers/text/textplain.h b/content/handlers/text/textplain.h
index b94ee33..6cc2716 100644
--- a/content/handlers/text/textplain.h
+++ b/content/handlers/text/textplain.h
@@ -26,9 +26,6 @@
#ifndef NETSURF_HTML_TEXTPLAIN_H
#define NETSURF_HTML_TEXTPLAIN_H
-struct content;
-struct rect;
-
/**
* Initialise the text content handler
*
@@ -37,13 +34,4 @@ struct rect;
nserror textplain_init(void);
-/**
- * Retrieve the size (in bytes) of text data
- *
- * \param[in] c Content to retrieve size of
- * \return Size, in bytes, of data
- */
-size_t textplain_size(struct content *c);
-
-
#endif
-----------------------------------------------------------------------
Summary of changes:
content/handlers/text/textplain.c | 21 +++++++++++----------
content/handlers/text/textplain.h | 12 ------------
2 files changed, 11 insertions(+), 22 deletions(-)
diff --git a/content/handlers/text/textplain.c b/content/handlers/text/textplain.c
index 21876ec..60051f5 100644
--- a/content/handlers/text/textplain.c
+++ b/content/handlers/text/textplain.c
@@ -1605,13 +1605,23 @@ textplain_textselection_copy(struct content *c,
return NSERROR_OK;
}
+
+/**
+ * Retrieve the index of the end of the text
+ *
+ * \param[in] c Content to retrieve size of
+ * \return Size, in bytes, of data
+ */
static nserror
textplain_textselection_get_end(struct content *c, unsigned *end_idx)
{
- *end_idx = textplain_size(c);
+ textplain_content *text = (textplain_content *)c;
+
+ *end_idx = text->utf8_data_size;
return NSERROR_OK;
}
+
/**
* plain text content handler table
*/
@@ -1671,12 +1681,3 @@ nserror textplain_init(void)
-/* exported interface documented in html/textplain.h */
-size_t textplain_size(struct content *c)
-{
- textplain_content *text = (textplain_content *) c;
-
- assert(c != NULL);
-
- return text->utf8_data_size;
-}
diff --git a/content/handlers/text/textplain.h b/content/handlers/text/textplain.h
index b94ee33..6cc2716 100644
--- a/content/handlers/text/textplain.h
+++ b/content/handlers/text/textplain.h
@@ -26,9 +26,6 @@
#ifndef NETSURF_HTML_TEXTPLAIN_H
#define NETSURF_HTML_TEXTPLAIN_H
-struct content;
-struct rect;
-
/**
* Initialise the text content handler
*
@@ -37,13 +34,4 @@ struct rect;
nserror textplain_init(void);
-/**
- * Retrieve the size (in bytes) of text data
- *
- * \param[in] c Content to retrieve size of
- * \return Size, in bytes, of data
- */
-size_t textplain_size(struct content *c);
-
-
#endif
--
NetSurf Browser
2 years, 10 months
netsurf: branch master updated. release/3.10-41-g652078f
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/652078f8cf098c1afb6e7...
...commit http://git.netsurf-browser.org/netsurf.git/commit/652078f8cf098c1afb6e7c9...
...tree http://git.netsurf-browser.org/netsurf.git/tree/652078f8cf098c1afb6e7c9df...
The branch, master has been updated
via 652078f8cf098c1afb6e7c9dfcb0d70415db301c (commit)
from 313e5cabba172c79a0a8d2bab0b8a99c204616fc (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=652078f8cf098c1afb6...
commit 652078f8cf098c1afb6e7c9dfcb0d70415db301c
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
fix erronenous child variable assignment
diff --git a/content/handlers/html/textselection.c b/content/handlers/html/textselection.c
index cc08aef..9de7590 100644
--- a/content/handlers/html/textselection.c
+++ b/content/handlers/html/textselection.c
@@ -448,7 +448,7 @@ static unsigned selection_label_subtree(struct box *box, unsigned idx)
assert(box != NULL);
- box = box->children;
+ child = box->children;
box->byte_offset = idx;
-----------------------------------------------------------------------
Summary of changes:
content/handlers/html/textselection.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/content/handlers/html/textselection.c b/content/handlers/html/textselection.c
index cc08aef..9de7590 100644
--- a/content/handlers/html/textselection.c
+++ b/content/handlers/html/textselection.c
@@ -448,7 +448,7 @@ static unsigned selection_label_subtree(struct box *box, unsigned idx)
assert(box != NULL);
- box = box->children;
+ child = box->children;
box->byte_offset = idx;
--
NetSurf Browser
2 years, 10 months
netsurf: branch master updated. release/3.10-40-g313e5ca
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/313e5cabba172c79a0a8d...
...commit http://git.netsurf-browser.org/netsurf.git/commit/313e5cabba172c79a0a8d2b...
...tree http://git.netsurf-browser.org/netsurf.git/tree/313e5cabba172c79a0a8d2bab...
The branch, master has been updated
via 313e5cabba172c79a0a8d2bab0b8a99c204616fc (commit)
from aa0b916492b5be45cb37a579e8bc9ab98e5b68d1 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=313e5cabba172c79a0a...
commit 313e5cabba172c79a0a8d2bab0b8a99c204616fc
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
ensure the html layout is present for text selection
The text selection operations can be called regardless
of when the html layout box tree is actually
available (e.g. if it is still loading when opened)
This change ensures the layout box tree is available before
attempting to traverse it for a selection operation.
diff --git a/content/handlers/html/textselection.c b/content/handlers/html/textselection.c
index 51c6ce1..cc08aef 100644
--- a/content/handlers/html/textselection.c
+++ b/content/handlers/html/textselection.c
@@ -444,7 +444,11 @@ selection_copy(struct box *box,
*/
static unsigned selection_label_subtree(struct box *box, unsigned idx)
{
- struct box *child = box->children;
+ struct box *child;
+
+ assert(box != NULL);
+
+ box = box->children;
box->byte_offset = idx;
@@ -475,6 +479,10 @@ html_textselection_redraw(struct content *c,
html_content *html = (html_content *)c;
struct rdw_info rdw;
+ if (html->layout == NULL) {
+ return NSERROR_INVALID;
+ }
+
rdw.inited = false;
res = coords_from_range(html->layout, start_idx, end_idx, &rdw, false);
@@ -505,6 +513,10 @@ html_textselection_copy(struct content *c,
save_text_whitespace before = WHITESPACE_NONE;
bool first = true;
+ if (html->layout == NULL) {
+ return NSERROR_INVALID;
+ }
+
return selection_copy(html->layout,
&html->len_ctx,
start_idx,
@@ -523,6 +535,10 @@ html_textselection_get_end(struct content *c, unsigned *end_idx)
html_content *html = (html_content *)c;
unsigned root_idx;
+ if (html->layout == NULL) {
+ return NSERROR_INVALID;
+ }
+
root_idx = 0;
*end_idx = selection_label_subtree(html->layout, root_idx);
-----------------------------------------------------------------------
Summary of changes:
content/handlers/html/textselection.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/content/handlers/html/textselection.c b/content/handlers/html/textselection.c
index 51c6ce1..cc08aef 100644
--- a/content/handlers/html/textselection.c
+++ b/content/handlers/html/textselection.c
@@ -444,7 +444,11 @@ selection_copy(struct box *box,
*/
static unsigned selection_label_subtree(struct box *box, unsigned idx)
{
- struct box *child = box->children;
+ struct box *child;
+
+ assert(box != NULL);
+
+ box = box->children;
box->byte_offset = idx;
@@ -475,6 +479,10 @@ html_textselection_redraw(struct content *c,
html_content *html = (html_content *)c;
struct rdw_info rdw;
+ if (html->layout == NULL) {
+ return NSERROR_INVALID;
+ }
+
rdw.inited = false;
res = coords_from_range(html->layout, start_idx, end_idx, &rdw, false);
@@ -505,6 +513,10 @@ html_textselection_copy(struct content *c,
save_text_whitespace before = WHITESPACE_NONE;
bool first = true;
+ if (html->layout == NULL) {
+ return NSERROR_INVALID;
+ }
+
return selection_copy(html->layout,
&html->len_ctx,
start_idx,
@@ -523,6 +535,10 @@ html_textselection_get_end(struct content *c, unsigned *end_idx)
html_content *html = (html_content *)c;
unsigned root_idx;
+ if (html->layout == NULL) {
+ return NSERROR_INVALID;
+ }
+
root_idx = 0;
*end_idx = selection_label_subtree(html->layout, root_idx);
--
NetSurf Browser
2 years, 10 months
libnsgif: branch master updated. release/0.2.1-2-ge5802bc
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/e5802bc82dd3b85eec48...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/e5802bc82dd3b85eec4843...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/e5802bc82dd3b85eec48437c...
The branch, master has been updated
via e5802bc82dd3b85eec48437c44a299457f6b3b3c (commit)
from 8442a27c2bb8df48029ceea6e64c4930106a57fc (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=e5802bc82dd3b85eec...
commit e5802bc82dd3b85eec48437c44a299457f6b3b3c
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Fix allocation size comparison in previous frame recorder.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 95bbbfb..479d161 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -589,7 +589,7 @@ static void gif__record_previous_frame(gif_animation *gif)
}
if (gif->prev_frame != NULL &&
- gif->width * gif->height < gif->prev_width * gif->prev_height) {
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
need_alloc = true;
}
-----------------------------------------------------------------------
Summary of changes:
src/libnsgif.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 95bbbfb..479d161 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -589,7 +589,7 @@ static void gif__record_previous_frame(gif_animation *gif)
}
if (gif->prev_frame != NULL &&
- gif->width * gif->height < gif->prev_width * gif->prev_height) {
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
need_alloc = true;
}
--
NetSurf GIF Decoder
2 years, 11 months
libnsgif: branch master updated. release/0.2.1-1-g8442a27
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/8442a27c2bb8df48029c...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/8442a27c2bb8df48029cee...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/8442a27c2bb8df48029ceea6...
The branch, master has been updated
via 8442a27c2bb8df48029ceea6e64c4930106a57fc (commit)
from a937d161f6787ecc1c1bef9bad6039925ff13d72 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=8442a27c2bb8df4802...
commit 8442a27c2bb8df48029ceea6e64c4930106a57fc
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Disposal Method: Handle Restore to previous with saved image.
Previously we decoded a previous frame over the current frame data
to handle resoration. However, the previous frame depended on its
own previous frame state for correct decode.
Now we just make a copy of the previous frame data and copy it
back to handle the GIF_FRAME_RESTORE case.
See: https://github.com/libvips/libvips/issues/1084#issuecomment-653497200
diff --git a/include/libnsgif.h b/include/libnsgif.h
index a819fec..50dc688 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -136,6 +136,15 @@ typedef struct gif_animation {
unsigned int *global_colour_table;
/** local colour table */
unsigned int *local_colour_table;
+
+ /** previous frame for GIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ int prev_index;
+ /** previous frame width */
+ unsigned prev_width;
+ /** previous frame height */
+ unsigned prev_height;
} gif_animation;
/**
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6bf9956..95bbbfb 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -570,6 +570,73 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
return g_res[l_res];
}
+static void gif__record_previous_frame(gif_animation *gif)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ const uint32_t *frame_data;
+ uint32_t *prev_frame;
+
+ if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
+
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
+ if (!frame_data) {
+ return;
+ }
+
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height < gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
+
+ if (need_alloc) {
+ prev_frame = realloc(gif->prev_frame,
+ gif->width * gif->height * 4);
+ if (prev_frame == NULL) {
+ return;
+ }
+ } else {
+ prev_frame = gif->prev_frame;
+ }
+
+ memcpy(prev_frame, frame_data, gif->width * gif->height * 4);
+
+ gif->prev_frame = prev_frame;
+ gif->prev_width = gif->width;
+ gif->prev_height = gif->height;
+ gif->prev_index = gif->decoded_frame;
+}
+
+static gif_result gif__recover_previous_frame(const gif_animation *gif)
+{
+ const uint32_t *prev_frame = gif->prev_frame;
+ unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
+ unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
+ uint32_t *frame_data;
+
+ if (prev_frame == NULL) {
+ return GIF_FRAME_DATA_ERROR;
+ }
+
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
+ if (!frame_data) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(frame_data, prev_frame, width * 4);
+
+ frame_data += gif->width;
+ prev_frame += gif->prev_width;
+ }
+
+ return GIF_OK;
+}
/**
* decode a gif frame
@@ -583,6 +650,7 @@ gif_internal_decode_frame(gif_animation *gif,
unsigned int frame,
bool clear_image)
{
+ gif_result err;
unsigned int index = 0;
unsigned char *gif_data, *gif_end;
int gif_bytes;
@@ -612,6 +680,11 @@ gif_internal_decode_frame(gif_animation *gif,
return GIF_OK;
}
+ if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) {
+ /* Store the previous frame for later restoration */
+ gif__record_previous_frame(gif);
+ }
+
/* Get the start of our frame data and the end of the GIF data */
gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
gif_end = gif->gif_data + gif->buffer_size;
@@ -788,34 +861,16 @@ gif_internal_decode_frame(gif_animation *gif,
(gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
/*
* If the previous frame's disposal method requires we
- * restore the previous image, find the last image set
- * to "do not dispose" and get that frame data
+ * restore the previous image, restore our saved image.
*/
- int last_undisposed_frame = frame - 2;
- while ((last_undisposed_frame >= 0) &&
- (gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) {
- last_undisposed_frame--;
- }
-
- /* If we don't find one, clear the frame data */
- if (last_undisposed_frame == -1) {
+ err = gif__recover_previous_frame(gif);
+ if (err != GIF_OK) {
/* see notes above on transparency
* vs. background color
*/
memset((char*)frame_data,
GIF_TRANSPARENT_COLOUR,
gif->width * gif->height * sizeof(int));
- } else {
- return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false);
- if (return_value != GIF_OK) {
- goto gif_decode_frame_exit;
- }
- /* Get this frame's data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
- }
}
}
gif->decoded_frame = frame;
@@ -923,6 +978,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
memset(gif, 0, sizeof(gif_animation));
gif->bitmap_callbacks = *bitmap_callbacks;
gif->decoded_frame = GIF_INVALID_FRAME;
+ gif->prev_index = GIF_INVALID_FRAME;
}
@@ -1164,6 +1220,9 @@ void gif_finalise(gif_animation *gif)
free(gif->global_colour_table);
gif->global_colour_table = NULL;
+ free(gif->prev_frame);
+ gif->prev_frame = NULL;
+
lzw_context_destroy(gif->lzw_ctx);
gif->lzw_ctx = NULL;
}
-----------------------------------------------------------------------
Summary of changes:
include/libnsgif.h | 9 +++++
src/libnsgif.c | 101 +++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 89 insertions(+), 21 deletions(-)
diff --git a/include/libnsgif.h b/include/libnsgif.h
index a819fec..50dc688 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -136,6 +136,15 @@ typedef struct gif_animation {
unsigned int *global_colour_table;
/** local colour table */
unsigned int *local_colour_table;
+
+ /** previous frame for GIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ int prev_index;
+ /** previous frame width */
+ unsigned prev_width;
+ /** previous frame height */
+ unsigned prev_height;
} gif_animation;
/**
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6bf9956..95bbbfb 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -570,6 +570,73 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
return g_res[l_res];
}
+static void gif__record_previous_frame(gif_animation *gif)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ const uint32_t *frame_data;
+ uint32_t *prev_frame;
+
+ if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
+
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
+ if (!frame_data) {
+ return;
+ }
+
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height < gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
+
+ if (need_alloc) {
+ prev_frame = realloc(gif->prev_frame,
+ gif->width * gif->height * 4);
+ if (prev_frame == NULL) {
+ return;
+ }
+ } else {
+ prev_frame = gif->prev_frame;
+ }
+
+ memcpy(prev_frame, frame_data, gif->width * gif->height * 4);
+
+ gif->prev_frame = prev_frame;
+ gif->prev_width = gif->width;
+ gif->prev_height = gif->height;
+ gif->prev_index = gif->decoded_frame;
+}
+
+static gif_result gif__recover_previous_frame(const gif_animation *gif)
+{
+ const uint32_t *prev_frame = gif->prev_frame;
+ unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
+ unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
+ uint32_t *frame_data;
+
+ if (prev_frame == NULL) {
+ return GIF_FRAME_DATA_ERROR;
+ }
+
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
+ if (!frame_data) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(frame_data, prev_frame, width * 4);
+
+ frame_data += gif->width;
+ prev_frame += gif->prev_width;
+ }
+
+ return GIF_OK;
+}
/**
* decode a gif frame
@@ -583,6 +650,7 @@ gif_internal_decode_frame(gif_animation *gif,
unsigned int frame,
bool clear_image)
{
+ gif_result err;
unsigned int index = 0;
unsigned char *gif_data, *gif_end;
int gif_bytes;
@@ -612,6 +680,11 @@ gif_internal_decode_frame(gif_animation *gif,
return GIF_OK;
}
+ if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) {
+ /* Store the previous frame for later restoration */
+ gif__record_previous_frame(gif);
+ }
+
/* Get the start of our frame data and the end of the GIF data */
gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
gif_end = gif->gif_data + gif->buffer_size;
@@ -788,34 +861,16 @@ gif_internal_decode_frame(gif_animation *gif,
(gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
/*
* If the previous frame's disposal method requires we
- * restore the previous image, find the last image set
- * to "do not dispose" and get that frame data
+ * restore the previous image, restore our saved image.
*/
- int last_undisposed_frame = frame - 2;
- while ((last_undisposed_frame >= 0) &&
- (gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) {
- last_undisposed_frame--;
- }
-
- /* If we don't find one, clear the frame data */
- if (last_undisposed_frame == -1) {
+ err = gif__recover_previous_frame(gif);
+ if (err != GIF_OK) {
/* see notes above on transparency
* vs. background color
*/
memset((char*)frame_data,
GIF_TRANSPARENT_COLOUR,
gif->width * gif->height * sizeof(int));
- } else {
- return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false);
- if (return_value != GIF_OK) {
- goto gif_decode_frame_exit;
- }
- /* Get this frame's data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
- }
}
}
gif->decoded_frame = frame;
@@ -923,6 +978,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
memset(gif, 0, sizeof(gif_animation));
gif->bitmap_callbacks = *bitmap_callbacks;
gif->decoded_frame = GIF_INVALID_FRAME;
+ gif->prev_index = GIF_INVALID_FRAME;
}
@@ -1164,6 +1220,9 @@ void gif_finalise(gif_animation *gif)
free(gif->global_colour_table);
gif->global_colour_table = NULL;
+ free(gif->prev_frame);
+ gif->prev_frame = NULL;
+
lzw_context_destroy(gif->lzw_ctx);
gif->lzw_ctx = NULL;
}
--
NetSurf GIF Decoder
2 years, 11 months
netsurf: branch master updated. release/3.10-38-g0908925
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/0908925ca67652e4e335c...
...commit http://git.netsurf-browser.org/netsurf.git/commit/0908925ca67652e4e335c5f...
...tree http://git.netsurf-browser.org/netsurf.git/tree/0908925ca67652e4e335c5f8e...
The branch, master has been updated
via 0908925ca67652e4e335c5f8ef4139ca6f60904f (commit)
from 1697118e227406764d1cfad78a5fcc9febe152be (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=0908925ca67652e4e33...
commit 0908925ca67652e4e335c5f8ef4139ca6f60904f
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
move the file fetcher sources into a single directory
diff --git a/content/Makefile b/content/Makefile
index abc5a24..188d0f4 100644
--- a/content/Makefile
+++ b/content/Makefile
@@ -3,7 +3,6 @@
S_CONTENT := \
content.c \
content_factory.c \
- dirlist.c \
fetch.c \
hlcache.c \
llcache.c \
@@ -18,10 +17,12 @@ ifeq ($(NETSURF_FS_BACKING_STORE),YES)
endif
-# Content fetchers sources
+# Content fetcher sources
include content/fetchers/Makefile
-# Content handlers
+S_FETCHERS := $(addprefix content/,$(S_FETCHERS))
+
+# Content handler sources
include content/handlers/Makefile
S_CONTENT := $(addprefix content/,$(S_CONTENT))
diff --git a/content/dirlist.c b/content/dirlist.c
deleted file mode 100644
index 3f79e65..0000000
--- a/content/dirlist.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright 2010 Michael Drake <tlsa(a)netsurf-browser.org>
- *
- * This file is part of NetSurf, http://www.netsurf-browser.org/
- *
- * NetSurf is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * NetSurf is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/** \file
- * Generate HTML content for displaying directory listings (implementation).
- */
-
-#include <stdbool.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "utils/nsurl.h"
-#include "utils/messages.h"
-#include "utils/nscolour.h"
-
-#include "netsurf/types.h"
-#include "netsurf/plot_style.h"
-
-#include "content/dirlist.h"
-#include "desktop/system_colour.h"
-
-static int dirlist_filesize_calculate(unsigned long *bytesize);
-static int dirlist_filesize_value(unsigned long bytesize);
-static char* dirlist_filesize_unit(unsigned long bytesize);
-
-
-/**
- * Generates the top part of an HTML directory listing page
- *
- * \return Top of directory listing HTML
- *
- * This is part of a series of functions. To generate a complete page,
- * call the following functions in order:
- *
- * dirlist_generate_top()
- * dirlist_generate_hide_columns() -- optional
- * dirlist_generate_title()
- * dirlist_generate_parent_link() -- optional
- * dirlist_generate_headings()
- * dirlist_generate_row() -- call 'n' times for 'n' rows
- * dirlist_generate_bottom()
- */
-
-bool dirlist_generate_top(char *buffer, int buffer_length)
-{
- int error = snprintf(buffer, buffer_length,
- "<html>\n"
- "<head>\n"
- "<link rel=\"stylesheet\" title=\"Standard\" "
- "type=\"text/css\" href=\"resource:internal.css\">\n"
- "<style>\n");
- if (error < 0 || error >= buffer_length)
- /* Error or buffer too small */
- return false;
- else
- /* OK */
- return true;
-
-}
-
-
-/**
- * Generates the part of an HTML directory listing page that can suppress
- * particular columns
- *
- * \param flags flags for which cols to suppress. 0 to suppress none
- * \param buffer buffer to fill with generated HTML
- * \param buffer_length maximum size of buffer
- * \return true iff buffer filled without error
- *
- * This is part of a series of functions. To generate a complete page,
- * call the following functions in order:
- *
- * dirlist_generate_top()
- * dirlist_generate_hide_columns() -- optional
- * dirlist_generate_title()
- * dirlist_generate_parent_link() -- optional
- * dirlist_generate_headings()
- * dirlist_generate_row() -- call 'n' times for 'n' rows
- * dirlist_generate_bottom()
- */
-
-bool dirlist_generate_hide_columns(int flags, char *buffer, int buffer_length)
-{
- int error = snprintf(buffer, buffer_length,
- "%s\n%s\n%s\n%s\n%s\n",
- (flags & DIRLIST_NO_NAME_COLUMN) ?
- "span.name { display: none; }\n" : "",
- (flags & DIRLIST_NO_TYPE_COLUMN) ?
- "span.type { display: none; }\n" : "",
- (flags & DIRLIST_NO_SIZE_COLUMN) ?
- "span.size { display: none; }\n" : "",
- (flags & DIRLIST_NO_DATE_COLUMN) ?
- "span.date { display: none; }\n" : "",
- (flags & DIRLIST_NO_TIME_COLUMN) ?
- "span.time { display: none; }\n" : "");
- if (error < 0 || error >= buffer_length)
- /* Error or buffer too small */
- return false;
- else
- /* OK */
- return true;
-}
-
-
-/**
- * Generates the part of an HTML directory listing page that contains the title
- *
- * \param title title to use
- * \param buffer buffer to fill with generated HTML
- * \param buffer_length maximum size of buffer
- * \return true iff buffer filled without error
- *
- * This is part of a series of functions. To generate a complete page,
- * call the following functions in order:
- *
- * dirlist_generate_top()
- * dirlist_generate_hide_columns() -- optional
- * dirlist_generate_title()
- * dirlist_generate_parent_link() -- optional
- * dirlist_generate_headings()
- * dirlist_generate_row() -- call 'n' times for 'n' rows
- * dirlist_generate_bottom()
- */
-
-bool dirlist_generate_title(const char *title, char *buffer, int buffer_length)
-{
- const char *stylesheet;
- nserror err;
- int error;
-
- if (title == NULL)
- title = "";
-
- err = nscolour_get_stylesheet(&stylesheet);
- if (err != NSERROR_OK) {
- return false;
- }
-
- error = snprintf(buffer, buffer_length,
- "</style>\n"
- "<title>%s</title>\n"
- "<style>\n"
- "html {\n"
- "\tbackground-color: #%06x;\n"
- "}\n"
- "%s"
- "</style>\n"
- "</head>\n"
- "<body id=\"dirlist\" class=\"ns-even-bg ns-even-fg ns-border\">\n"
- "<h1 class=\"ns-border\">%s</h1>\n",
- title,
- colour_rb_swap(nscolours[NSCOLOUR_WIN_ODD_BG]),
- stylesheet, title);
- if (error < 0 || error >= buffer_length)
- /* Error or buffer too small */
- return false;
- else
- /* OK */
- return true;
-}
-
-
-/**
- * Generates the part of an HTML directory listing page that links to the parent
- * directory
- *
- * \param parent url of parent directory
- * \param buffer buffer to fill with generated HTML
- * \param buffer_length maximum size of buffer
- * \return true iff buffer filled without error
- *
- * This is part of a series of functions. To generate a complete page,
- * call the following functions in order:
- *
- * dirlist_generate_top()
- * dirlist_generate_hide_columns() -- optional
- * dirlist_generate_title()
- * dirlist_generate_parent_link() -- optional
- * dirlist_generate_headings()
- * dirlist_generate_row() -- call 'n' times for 'n' rows
- * dirlist_generate_bottom()
- */
-
-bool dirlist_generate_parent_link(const char *parent, char *buffer,
- int buffer_length)
-{
- int error = snprintf(buffer, buffer_length,
- "<p><a href=\"%s\">%s</a></p>",
- parent, messages_get("FileParent"));
- if (error < 0 || error >= buffer_length)
- /* Error or buffer too small */
- return false;
- else
- /* OK */
- return true;
-}
-
-
-/**
- * Generates the part of an HTML directory listing page that displays the column
- * headings
- *
- * \param buffer buffer to fill with generated HTML
- * \param buffer_length maximum size of buffer
- * \return true iff buffer filled without error
- *
- * This is part of a series of functions. To generate a complete page,
- * call the following functions in order:
- *
- * dirlist_generate_top()
- * dirlist_generate_hide_columns() -- optional
- * dirlist_generate_title()
- * dirlist_generate_parent_link() -- optional
- * dirlist_generate_headings()
- * dirlist_generate_row() -- call 'n' times for 'n' rows
- * dirlist_generate_bottom()
- */
-
-bool dirlist_generate_headings(char *buffer, int buffer_length)
-{
- int error = snprintf(buffer, buffer_length,
- "<div>\n"
- "<strong>\n"
- "\t<span class=\"name\">%s</span>\n"
- "\t<span class=\"type\">%s</span>\n"
- "\t<span class=\"size\">%s</span>"
- "<span class=\"size\"></span>\n"
- "\t<span class=\"date\">%s</span>\n"
- "\t<span class=\"time\">%s</span>\n"
- "</strong>\n",
- messages_get("FileName"), messages_get("FileType"),
- messages_get("FileSize"), messages_get("FileDate"),
- messages_get("FileTime"));
- if (error < 0 || error >= buffer_length)
- /* Error or buffer too small */
- return false;
- else
- /* OK */
- return true;
-}
-
-
-/**
- * Generates the part of an HTML directory listing page that displays a row
- * in the directory contents table
- *
- * \param even evenness of row number, for alternate row colouring
- * \param directory whether this row is for a directory (or a file)
- * \param url url for row entry
- * \param name name of row entry
- * \param mimetype MIME type of row entry
- * \param size size of row entry. If negative, size is left blank
- * \param date date row entry was last modified
- * \param time time row entry was last modified
- * \param buffer buffer to fill with generated HTML
- * \param buffer_length maximum size of buffer
- * \return true iff buffer filled without error
- *
- * This is part of a series of functions. To generate a complete page,
- * call the following functions in order:
- *
- * dirlist_generate_top()
- * dirlist_generate_hide_columns() -- optional
- * dirlist_generate_title()
- * dirlist_generate_parent_link() -- optional
- * dirlist_generate_headings()
- * dirlist_generate_row() -- call 'n' times for 'n' rows
- * dirlist_generate_bottom()
- */
-
-bool dirlist_generate_row(bool even, bool directory, nsurl *url, char *name,
- const char *mimetype, long long size, char *date, char *time,
- char *buffer, int buffer_length)
-{
- const char *unit;
- char size_string[100];
- int error;
-
- if (size < 0) {
- unit = "";
- strncpy(size_string, "", sizeof size_string);
- } else {
- unit = messages_get(dirlist_filesize_unit((unsigned long)size));
- snprintf(size_string, sizeof size_string, "%d",
- dirlist_filesize_value((unsigned long)size));
- }
-
- error = snprintf(buffer, buffer_length,
- "<a href=\"%s\" class=\"%s %s\">\n"
- "\t<span class=\"name ns-border\">%s</span>\n"
- "\t<span class=\"type ns-border\">%s</span>\n"
- "\t<span class=\"size ns-border\">%s</span>"
- "<span class=\"size ns-border\">%s</span>\n"
- "\t<span class=\"date ns-border\">%s</span>\n"
- "\t<span class=\"time ns-border\">%s</span>\n"
- "</a>\n", nsurl_access(url),
- even ? "even ns-even-bg" : "odd ns-odd-bg",
- directory ? "dir" : "file",
- name, mimetype, size_string, unit, date, time);
- if (error < 0 || error >= buffer_length)
- /* Error or buffer too small */
- return false;
- else
- /* OK */
- return true;
-}
-
-
-/**
- * Generates the bottom part of an HTML directory listing page
- *
- * \return Bottom of directory listing HTML
- *
- * This is part of a series of functions. To generate a complete page,
- * call the following functions in order:
- *
- * dirlist_generate_top()
- * dirlist_generate_hide_columns() -- optional
- * dirlist_generate_title()
- * dirlist_generate_parent_link() -- optional
- * dirlist_generate_headings()
- * dirlist_generate_row() -- call 'n' times for 'n' rows
- * dirlist_generate_bottom()
- */
-
-bool dirlist_generate_bottom(char *buffer, int buffer_length)
-{
- int error = snprintf(buffer, buffer_length,
- "</div>\n"
- "</body>\n"
- "</html>\n");
- if (error < 0 || error >= buffer_length)
- /* Error or buffer too small */
- return false;
- else
- /* OK */
- return true;
-}
-
-
-/**
- * Obtain display value and units for filesize after conversion to B/kB/MB/GB,
- * as appropriate.
- *
- * \param bytesize file size in bytes, updated to filesize in output units
- * \return number of times bytesize has been divided by 1024
- */
-
-int dirlist_filesize_calculate(unsigned long *bytesize)
-{
- int i = 0;
- while (*bytesize > 1024 * 4) {
- *bytesize /= 1024;
- i++;
- if (i == 3)
- break;
- }
- return i;
-}
-
-
-/**
- * Obtain display value for filesize after conversion to B/kB/MB/GB,
- * as appropriate
- *
- * \param bytesize file size in bytes
- * \return Value to display for file size, in units given by filesize_unit()
- */
-
-int dirlist_filesize_value(unsigned long bytesize)
-{
- dirlist_filesize_calculate(&bytesize);
- return (int)bytesize;
-}
-
-
-/**
- * Obtain display units for filesize after conversion to B/kB/MB/GB,
- * as appropriate
- *
- * \param bytesize file size in bytes
- * \return Units to display for file size, for value given by filesize_value()
- */
-
-char* dirlist_filesize_unit(unsigned long bytesize)
-{
- const char* units[] = { "Bytes", "kBytes", "MBytes", "GBytes" };
- return (char*)units[dirlist_filesize_calculate(&bytesize)];
-}
diff --git a/content/dirlist.h b/content/dirlist.h
deleted file mode 100644
index 5cdaf75..0000000
--- a/content/dirlist.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2010 Michael Drake <tlsa(a)netsurf-browser.org>
- *
- * This file is part of NetSurf, http://www.netsurf-browser.org/
- *
- * NetSurf is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * NetSurf is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/** \file
- * Generate HTML content for displaying directory listings (interface).
- *
- * These functions should in general be called via the content interface.
- */
-
-#ifndef _NETSURF_CONTENT_DIRLIST_H_
-#define _NETSURF_CONTENT_DIRLIST_H_
-
-#include <stdbool.h>
-
-#define DIRLIST_NO_NAME_COLUMN 1
-#define DIRLIST_NO_TYPE_COLUMN 1 << 1
-#define DIRLIST_NO_SIZE_COLUMN 1 << 2
-#define DIRLIST_NO_DATE_COLUMN 1 << 3
-#define DIRLIST_NO_TIME_COLUMN 1 << 4
-
-struct nsurl;
-
-bool dirlist_generate_top(char *buffer, int buffer_length);
-bool dirlist_generate_hide_columns(int flags, char *buffer, int buffer_length);
-bool dirlist_generate_title(const char *title, char *buffer, int buffer_length);
-bool dirlist_generate_parent_link(const char *parent, char *buffer,
- int buffer_length);
-bool dirlist_generate_headings(char *buffer, int buffer_length);
-bool dirlist_generate_row(bool even, bool directory, struct nsurl *url,
- char *name, const char *mimetype, long long size, char *date,
- char *time, char *buffer, int buffer_length);
-bool dirlist_generate_bottom(char *buffer, int buffer_length);
-
-#endif
diff --git a/content/fetch.c b/content/fetch.c
index 4cc7859..a260799 100644
--- a/content/fetch.c
+++ b/content/fetch.c
@@ -56,7 +56,7 @@
#include "content/fetchers/about.h"
#include "content/fetchers/curl.h"
#include "content/fetchers/data.h"
-#include "content/fetchers/file.h"
+#include "content/fetchers/file/file.h"
#include "javascript/fetcher.h"
#include "content/urldb.h"
diff --git a/content/fetchers/Makefile b/content/fetchers/Makefile
index 9c84793..e87a4e8 100644
--- a/content/fetchers/Makefile
+++ b/content/fetchers/Makefile
@@ -1,10 +1,15 @@
# Content fetchers sources
-S_FETCHERS_YES := data.c file.c about.c resource.c
+S_FETCHERS_YES := data.c about.c resource.c
S_FETCHERS_NO :=
S_FETCHERS_$(NETSURF_USE_CURL) += curl.c
-S_FETCHERS := $(addprefix content/fetchers/,$(S_FETCHERS_YES))
+S_FETCHERS := $(addprefix fetchers/,$(S_FETCHERS_YES))
+
+# File fetcher
+include content/fetchers/file/Makefile
+
+S_FETCHERS += $(addprefix fetchers/file/,$(S_FETCHER_FILE))
# The following files depend on the testament
content/fetchers/about.c: testament $(OBJROOT)/testament.h
diff --git a/content/fetchers/file.c b/content/fetchers/file.c
deleted file mode 100644
index bf2cc32..0000000
--- a/content/fetchers/file.c
+++ /dev/null
@@ -1,844 +0,0 @@
-/*
- * Copyright 2010 Vincent Sanders <vince(a)netsurf-browser.org>
- *
- * This file is part of NetSurf.
- *
- * NetSurf is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * NetSurf is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * \file
- *
- * file scheme URL handling. Based on the data fetcher by Rob Kendrick
- *
- * output dates and directory ordering are affected by the current locale
- */
-
-#include "utils/config.h"
-
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdbool.h>
-#include <string.h>
-#include <strings.h>
-#include <time.h>
-#include <stdio.h>
-#include <stdarg.h>
-#ifdef HAVE_MMAP
-#include <sys/mman.h>
-#endif
-#include <libwapcaplet/libwapcaplet.h>
-
-#include "netsurf/inttypes.h"
-#include "utils/nsurl.h"
-#include "utils/dirent.h"
-#include "utils/corestrings.h"
-#include "utils/messages.h"
-#include "utils/utils.h"
-#include "utils/log.h"
-#include "utils/time.h"
-#include "utils/ring.h"
-#include "utils/file.h"
-#include "netsurf/fetch.h"
-#include "desktop/gui_internal.h"
-
-#include "content/dirlist.h"
-#include "content/fetch.h"
-#include "content/fetchers.h"
-#include "content/fetchers/file.h"
-
-/* Maximum size of read buffer */
-#define FETCH_FILE_MAX_BUF_SIZE (1024 * 1024)
-
-/** Context for a fetch */
-struct fetch_file_context {
- struct fetch_file_context *r_next, *r_prev;
-
- struct fetch *fetchh; /**< Handle for this fetch */
-
- bool aborted; /**< Flag indicating fetch has been aborted */
- bool locked; /**< Flag indicating entry is already entered */
-
- nsurl *url; /**< The full url the fetch refers to */
- char *path; /**< The actual path to be used with open() */
-
- time_t file_etag; /**< Request etag for file (previous st.m_time) */
-};
-
-static struct fetch_file_context *ring = NULL;
-
-/** issue fetch callbacks with locking */
-static inline bool fetch_file_send_callback(const fetch_msg *msg,
- struct fetch_file_context *ctx)
-{
- ctx->locked = true;
- fetch_send_callback(msg, ctx->fetchh);
- ctx->locked = false;
-
- return ctx->aborted;
-}
-
-static bool fetch_file_send_header(struct fetch_file_context *ctx,
- const char *fmt, ...)
-{
- fetch_msg msg;
- char header[64];
- va_list ap;
- int len;
-
- va_start(ap, fmt);
- len = vsnprintf(header, sizeof header, fmt, ap);
- va_end(ap);
-
- if (len >= (int)sizeof(header) || len < 0) {
- return false;
- }
-
- msg.type = FETCH_HEADER;
- msg.data.header_or_data.buf = (const uint8_t *) header;
- msg.data.header_or_data.len = len;
-
- return fetch_file_send_callback(&msg, ctx);
-}
-
-/** callback to initialise the file fetcher. */
-static bool fetch_file_initialise(lwc_string *scheme)
-{
- return true;
-}
-
-/** callback to initialise the file fetcher. */
-static void fetch_file_finalise(lwc_string *scheme)
-{
-}
-
-static bool fetch_file_can_fetch(const nsurl *url)
-{
- return true;
-}
-
-/** callback to set up a file fetch context. */
-static void *
-fetch_file_setup(struct fetch *fetchh,
- nsurl *url,
- bool only_2xx,
- bool downgrade_tls,
- const char *post_urlenc,
- const struct fetch_multipart_data *post_multipart,
- const char **headers)
-{
- struct fetch_file_context *ctx;
- int i;
- nserror ret;
-
- ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL)
- return NULL;
-
- ret = guit->file->nsurl_to_path(url, &ctx->path);
- if (ret != NSERROR_OK) {
- free(ctx);
- return NULL;
- }
-
- ctx->url = nsurl_ref(url);
-
- /* Scan request headers looking for If-None-Match */
- for (i = 0; headers[i] != NULL; i++) {
- if (strncasecmp(headers[i], "If-None-Match:",
- SLEN("If-None-Match:")) != 0) {
- continue;
- }
-
- /* If-None-Match: "12345678" */
- const char *d = headers[i] + SLEN("If-None-Match:");
-
- /* Scan to first digit, if any */
- while (*d != '\0' && (*d < '0' || '9' < *d))
- d++;
-
- /* Convert to time_t */
- if (*d != '\0') {
- ret = nsc_snptimet(d, strlen(d), &ctx->file_etag);
- if (ret != NSERROR_OK) {
- NSLOG(fetch, WARNING,
- "Bad If-None-Match value");
- }
- }
- }
-
- ctx->fetchh = fetchh;
-
- RING_INSERT(ring, ctx);
-
- return ctx;
-}
-
-/** callback to free a file fetch */
-static void fetch_file_free(void *ctx)
-{
- struct fetch_file_context *c = ctx;
- nsurl_unref(c->url);
- free(c->path);
- free(ctx);
-}
-
-/** callback to start a file fetch */
-static bool fetch_file_start(void *ctx)
-{
- return true;
-}
-
-/** callback to abort a file fetch */
-static void fetch_file_abort(void *ctx)
-{
- struct fetch_file_context *c = ctx;
-
- /* To avoid the poll loop having to deal with the fetch context
- * disappearing from under it, we simply flag the abort here.
- * The poll loop itself will perform the appropriate cleanup.
- */
- c->aborted = true;
-}
-
-static int fetch_file_errno_to_http_code(int error_no)
-{
- switch (error_no) {
- case ENAMETOOLONG:
- return 400;
- case EACCES:
- return 403;
- case ENOENT:
- return 404;
- default:
- break;
- }
-
- return 500;
-}
-
-static void fetch_file_process_error(struct fetch_file_context *ctx, int code)
-{
- fetch_msg msg;
- char buffer[1024];
- const char *title;
- char key[8];
-
- /* content is going to return error code */
- fetch_set_http_code(ctx->fetchh, code);
-
- /* content type */
- if (fetch_file_send_header(ctx, "Content-Type: text/html"))
- goto fetch_file_process_error_aborted;
-
- snprintf(key, sizeof key, "HTTP%03d", code);
- title = messages_get(key);
-
- snprintf(buffer, sizeof buffer, "<html><head><title>%s</title></head>"
- "<body><h1>%s</h1>"
- "<p>Error %d while fetching file %s</p></body></html>",
- title, title, code, nsurl_access(ctx->url));
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buffer;
- msg.data.header_or_data.len = strlen(buffer);
- if (fetch_file_send_callback(&msg, ctx))
- goto fetch_file_process_error_aborted;
-
- msg.type = FETCH_FINISHED;
- fetch_file_send_callback(&msg, ctx);
-
-fetch_file_process_error_aborted:
- return;
-}
-
-
-/** Process object as a regular file */
-static void fetch_file_process_plain(struct fetch_file_context *ctx,
- struct stat *fdstat)
-{
-#ifdef HAVE_MMAP
- fetch_msg msg;
- char *buf = NULL;
- size_t buf_size;
-
- int fd; /**< The file descriptor of the object */
-
- /* Check if we can just return not modified */
- if (ctx->file_etag != 0 && ctx->file_etag == fdstat->st_mtime) {
- fetch_set_http_code(ctx->fetchh, 304);
- msg.type = FETCH_NOTMODIFIED;
- fetch_file_send_callback(&msg, ctx);
- return;
- }
-
- fd = open(ctx->path, O_RDONLY);
- if (fd < 0) {
- /* process errors as appropriate */
- fetch_file_process_error(ctx,
- fetch_file_errno_to_http_code(errno));
- return;
- }
-
- /* set buffer size */
- buf_size = fdstat->st_size;
-
- /* allocate the buffer storage */
- if (buf_size > 0) {
- buf = mmap(NULL, buf_size, PROT_READ, MAP_SHARED, fd, 0);
- if (buf == MAP_FAILED) {
- msg.type = FETCH_ERROR;
- msg.data.error = "Unable to map memory for file data buffer";
- fetch_file_send_callback(&msg, ctx);
- close(fd);
- return;
- }
- }
-
- /* fetch is going to be successful */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* Any callback can result in the fetch being aborted.
- * Therefore, we _must_ check for this after _every_ call to
- * fetch_file_send_callback().
- */
-
- /* content type */
- if (fetch_file_send_header(ctx, "Content-Type: %s",
- guit->fetch->filetype(ctx->path))) {
- goto fetch_file_process_aborted;
- }
-
- /* content length */
- if (fetch_file_send_header(ctx, "Content-Length: %" PRIsizet,
- fdstat->st_size)) {
- goto fetch_file_process_aborted;
- }
-
- /* create etag */
- if (fetch_file_send_header(ctx, "ETag: \"%10" PRId64 "\"",
- (int64_t) fdstat->st_mtime)) {
- goto fetch_file_process_aborted;
- }
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buf;
- msg.data.header_or_data.len = buf_size;
- fetch_file_send_callback(&msg, ctx);
-
- if (ctx->aborted == false) {
- msg.type = FETCH_FINISHED;
- fetch_file_send_callback(&msg, ctx);
- }
-
-fetch_file_process_aborted:
-
- if (buf != NULL)
- munmap(buf, buf_size);
- close(fd);
-#else
- fetch_msg msg;
- char *buf;
- size_t buf_size;
-
- ssize_t tot_read = 0;
- ssize_t res;
-
- FILE *infile;
-
- /* Check if we can just return not modified */
- if (ctx->file_etag != 0 && ctx->file_etag == fdstat->st_mtime) {
- fetch_set_http_code(ctx->fetchh, 304);
- msg.type = FETCH_NOTMODIFIED;
- fetch_file_send_callback(&msg, ctx);
- return;
- }
-
- infile = fopen(ctx->path, "rb");
- if (infile == NULL) {
- /* process errors as appropriate */
- fetch_file_process_error(ctx,
- fetch_file_errno_to_http_code(errno));
- return;
- }
-
- /* set buffer size */
- buf_size = fdstat->st_size;
- if (buf_size > FETCH_FILE_MAX_BUF_SIZE)
- buf_size = FETCH_FILE_MAX_BUF_SIZE;
-
- /* allocate the buffer storage */
- buf = malloc(buf_size);
- if (buf == NULL) {
- msg.type = FETCH_ERROR;
- msg.data.error =
- "Unable to allocate memory for file data buffer";
- fetch_file_send_callback(&msg, ctx);
- fclose(infile);
- return;
- }
-
- /* fetch is going to be successful */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* Any callback can result in the fetch being aborted.
- * Therefore, we _must_ check for this after _every_ call to
- * fetch_file_send_callback().
- */
-
- /* content type */
- if (fetch_file_send_header(ctx, "Content-Type: %s",
- guit->fetch->filetype(ctx->path))) {
- goto fetch_file_process_aborted;
- }
-
- /* content length */
- if (fetch_file_send_header(ctx, "Content-Length: %" PRIsizet,
- fdstat->st_size)) {
- goto fetch_file_process_aborted;
- }
-
- /* create etag */
- if (fetch_file_send_header(ctx, "ETag: \"%10" PRId64 "\"",
- (int64_t) fdstat->st_mtime)) {
- goto fetch_file_process_aborted;
- }
-
- /* main data loop */
- while (tot_read < fdstat->st_size) {
- res = fread(buf, 1, buf_size, infile);
- if (res == 0) {
- if (feof(infile)) {
- msg.type = FETCH_ERROR;
- msg.data.error = "Unexpected EOF reading file";
- fetch_file_send_callback(&msg, ctx);
- goto fetch_file_process_aborted;
- } else {
- msg.type = FETCH_ERROR;
- msg.data.error = "Error reading file";
- fetch_file_send_callback(&msg, ctx);
- goto fetch_file_process_aborted;
- }
- }
- tot_read += res;
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buf;
- msg.data.header_or_data.len = res;
- if (fetch_file_send_callback(&msg, ctx))
- break;
- }
-
- if (ctx->aborted == false) {
- msg.type = FETCH_FINISHED;
- fetch_file_send_callback(&msg, ctx);
- }
-
-fetch_file_process_aborted:
-
- fclose(infile);
- free(buf);
-#endif
- return;
-}
-
-static char *gen_nice_title(char *path)
-{
- char *nice_path, *cnv, *tmp;
- char *title;
- int title_length;
-
- /* Convert path for display */
- nice_path = malloc(strlen(path) * SLEN("&") + 1);
- if (nice_path == NULL) {
- return NULL;
- }
-
- /* Escape special HTML characters */
- for (cnv = nice_path, tmp = path; *tmp != '\0'; tmp++) {
- if (*tmp == '<') {
- *cnv++ = '&';
- *cnv++ = 'l';
- *cnv++ = 't';
- *cnv++ = ';';
- } else if (*tmp == '>') {
- *cnv++ = '&';
- *cnv++ = 'g';
- *cnv++ = 't';
- *cnv++ = ';';
- } else if (*tmp == '&') {
- *cnv++ = '&';
- *cnv++ = 'a';
- *cnv++ = 'm';
- *cnv++ = 'p';
- *cnv++ = ';';
- } else {
- *cnv++ = *tmp;
- }
- }
- *cnv = '\0';
-
- /* Construct a localised title string */
- title_length = (cnv - nice_path) + strlen(messages_get("FileIndex"));
- title = malloc(title_length + 1);
-
- if (title == NULL) {
- free(nice_path);
- return NULL;
- }
-
- /* Set title to localised "Index of <nice_path>" */
- snprintf(title, title_length, messages_get("FileIndex"), nice_path);
-
- free(nice_path);
-
- return title;
-}
-
-/**
- * Generate an output row of the directory listing.
- *
- * \param ctx The file fetching context.
- * \param ent current directory entry.
- * \param even is the row an even row.
- * \param buffer The output buffer.
- * \param buffer_len The space available in the output buffer.
- * \return NSERROR_OK or error code on faliure.
- */
-static nserror
-process_dir_ent(struct fetch_file_context *ctx,
- struct dirent *ent,
- bool even,
- char *buffer,
- size_t buffer_len)
-{
- nserror ret;
- char *urlpath = NULL; /* buffer for leaf entry path */
- struct stat ent_stat; /* stat result of leaf entry */
- char datebuf[64]; /* buffer for date text */
- char timebuf[64]; /* buffer for time text */
- nsurl *url;
-
- /* skip hidden files */
- if (ent->d_name[0] == '.') {
- return NSERROR_BAD_PARAMETER;
- }
-
- ret = netsurf_mkpath(&urlpath, NULL, 2, ctx->path, ent->d_name);
- if (ret != NSERROR_OK) {
- return ret;
- }
-
- if (stat(urlpath, &ent_stat) != 0) {
- ent_stat.st_mode = 0;
- datebuf[0] = 0;
- timebuf[0] = 0;
- } else {
- /* Get date in output format. a (day of week) and b
- * (month) are both affected by the locale
- */
- if (strftime((char *)&datebuf, sizeof datebuf, "%a %d %b %Y",
- localtime(&ent_stat.st_mtime)) == 0) {
- datebuf[0] = '-';
- datebuf[1] = 0;
- }
-
- /* Get time in output format */
- if (strftime((char *)&timebuf, sizeof timebuf, "%H:%M",
- localtime(&ent_stat.st_mtime)) == 0) {
- timebuf[0] = '-';
- timebuf[1] = 0;
- }
- }
-
- ret = guit->file->path_to_nsurl(urlpath, &url);
- if (ret != NSERROR_OK) {
- free(urlpath);
- return ret;
- }
-
- if (S_ISREG(ent_stat.st_mode)) {
- /* regular file */
- dirlist_generate_row(even,
- false,
- url,
- ent->d_name,
- guit->fetch->filetype(urlpath),
- ent_stat.st_size,
- datebuf, timebuf,
- buffer, buffer_len);
- } else if (S_ISDIR(ent_stat.st_mode)) {
- /* directory */
- dirlist_generate_row(even,
- true,
- url,
- ent->d_name,
- messages_get("FileDirectory"),
- -1,
- datebuf, timebuf,
- buffer, buffer_len);
- } else {
- /* something else */
- dirlist_generate_row(even,
- false,
- url,
- ent->d_name,
- "",
- -1,
- datebuf, timebuf,
- buffer, buffer_len);
- }
-
- nsurl_unref(url);
- free(urlpath);
-
- return NSERROR_OK;
-}
-
-/**
- * Comparison function for sorting directories.
- *
- * Correctly orders non zero-padded numerical parts.
- * ie. produces "file1, file2, file10" rather than "file1, file10, file2".
- *
- * \param d1 first directory entry
- * \param d2 second directory entry
- */
-static int dir_sort_alpha(const struct dirent **d1, const struct dirent **d2)
-{
- const char *s1 = (*d1)->d_name;
- const char *s2 = (*d2)->d_name;
-
- while (*s1 != '\0' && *s2 != '\0') {
- if ((*s1 >= '0' && *s1 <= '9') &&
- (*s2 >= '0' && *s2 <= '9')) {
- int n1 = 0, n2 = 0;
- while (*s1 >= '0' && *s1 <= '9') {
- n1 = n1 * 10 + (*s1) - '0';
- s1++;
- }
- while (*s2 >= '0' && *s2 <= '9') {
- n2 = n2 * 10 + (*s2) - '0';
- s2++;
- }
- if (n1 != n2) {
- return n1 - n2;
- }
- if (*s1 == '\0' || *s2 == '\0')
- break;
- }
- if (tolower(*s1) != tolower(*s2))
- break;
-
- s1++;
- s2++;
- }
-
- return tolower(*s1) - tolower(*s2);
-}
-
-static void fetch_file_process_dir(struct fetch_file_context *ctx,
- struct stat *fdstat)
-{
- fetch_msg msg;
- char buffer[1024]; /* Output buffer */
- bool even = false; /* formatting flag */
- char *title; /* pretty printed title */
- nserror err; /* result from url routines */
- nsurl *up; /* url of parent */
-
- struct dirent **listing = NULL; /* directory entry listing */
- int i; /* directory entry index */
- int n; /* number of directory entries */
-
- n = scandir(ctx->path, &listing, 0, dir_sort_alpha);
- if (n < 0) {
- fetch_file_process_error(ctx,
- fetch_file_errno_to_http_code(errno));
- return;
- }
-
- /* fetch is going to be successful */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* force no-cache */
- if (fetch_file_send_header(ctx, "Cache-Control: no-cache"))
- goto fetch_file_process_dir_aborted;
-
- /* content type */
- if (fetch_file_send_header(ctx, "Content-Type: text/html"))
- goto fetch_file_process_dir_aborted;
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buffer;
-
- /* directory listing top */
- dirlist_generate_top(buffer, sizeof buffer);
- msg.data.header_or_data.len = strlen(buffer);
- if (fetch_file_send_callback(&msg, ctx))
- goto fetch_file_process_dir_aborted;
-
- /* directory listing title */
- title = gen_nice_title(ctx->path);
- dirlist_generate_title(title, buffer, sizeof buffer);
- free(title);
- msg.data.header_or_data.len = strlen(buffer);
- if (fetch_file_send_callback(&msg, ctx))
- goto fetch_file_process_dir_aborted;
-
- /* Print parent directory link */
- err = nsurl_parent(ctx->url, &up);
- if (err == NSERROR_OK) {
- if (nsurl_compare(ctx->url, up, NSURL_COMPLETE) == false) {
- /* different URL; have parent */
- dirlist_generate_parent_link(nsurl_access(up),
- buffer, sizeof buffer);
-
- msg.data.header_or_data.len = strlen(buffer);
- fetch_file_send_callback(&msg, ctx);
- }
- nsurl_unref(up);
-
- if (ctx->aborted)
- goto fetch_file_process_dir_aborted;
-
- }
-
- /* directory list headings */
- dirlist_generate_headings(buffer, sizeof buffer);
- msg.data.header_or_data.len = strlen(buffer);
- if (fetch_file_send_callback(&msg, ctx))
- goto fetch_file_process_dir_aborted;
-
- for (i = 0; i < n; i++) {
-
- err = process_dir_ent(ctx, listing[i], even, buffer,
- sizeof(buffer));
-
- if (err == NSERROR_OK) {
- msg.data.header_or_data.len = strlen(buffer);
- if (fetch_file_send_callback(&msg, ctx))
- goto fetch_file_process_dir_aborted;
-
- even = !even;
- }
- }
-
- /* directory listing bottom */
- dirlist_generate_bottom(buffer, sizeof buffer);
- msg.data.header_or_data.len = strlen(buffer);
- if (fetch_file_send_callback(&msg, ctx))
- goto fetch_file_process_dir_aborted;
-
- msg.type = FETCH_FINISHED;
- fetch_file_send_callback(&msg, ctx);
-
-fetch_file_process_dir_aborted:
-
- if (listing != NULL) {
- for (i = 0; i < n; i++) {
- free(listing[i]);
- }
- free(listing);
- }
-}
-
-
-/* process a file fetch */
-static void fetch_file_process(struct fetch_file_context *ctx)
-{
- struct stat fdstat; /**< The objects stat */
-
- if (stat(ctx->path, &fdstat) != 0) {
- /* process errors as appropriate */
- fetch_file_process_error(ctx,
- fetch_file_errno_to_http_code(errno));
- return;
- }
-
- if (S_ISDIR(fdstat.st_mode)) {
- /* directory listing */
- fetch_file_process_dir(ctx, &fdstat);
- return;
- } else if (S_ISREG(fdstat.st_mode)) {
- /* regular file */
- fetch_file_process_plain(ctx, &fdstat);
- return;
- } else {
- /* unhandled type of file */
- fetch_file_process_error(ctx, 501);
- }
-
- return;
-}
-
-/** callback to poll for additional file fetch contents */
-static void fetch_file_poll(lwc_string *scheme)
-{
- struct fetch_file_context *c, *save_ring = NULL;
-
- while (ring != NULL) {
- /* Take the first entry from the ring */
- c = ring;
- RING_REMOVE(ring, c);
-
- /* Ignore fetches that have been flagged as locked.
- * This allows safe re-entrant calls to this function.
- * Re-entrancy can occur if, as a result of a callback,
- * the interested party causes fetch_poll() to be called
- * again.
- */
- if (c->locked == true) {
- RING_INSERT(save_ring, c);
- continue;
- }
-
- /* Only process non-aborted fetches */
- if (c->aborted == false) {
- /* file fetches can be processed in one go */
- fetch_file_process(c);
- }
-
- /* And now finish */
- fetch_remove_from_queues(c->fetchh);
- fetch_free(c->fetchh);
-
- }
-
- /* Finally, if we saved any fetches which were locked, put them back
- * into the ring for next time
- */
- ring = save_ring;
-}
-
-nserror fetch_file_register(void)
-{
- lwc_string *scheme = lwc_string_ref(corestring_lwc_file);
- const struct fetcher_operation_table fetcher_ops = {
- .initialise = fetch_file_initialise,
- .acceptable = fetch_file_can_fetch,
- .setup = fetch_file_setup,
- .start = fetch_file_start,
- .abort = fetch_file_abort,
- .free = fetch_file_free,
- .poll = fetch_file_poll,
- .finalise = fetch_file_finalise
- };
-
- return fetcher_add(scheme, &fetcher_ops);
-}
diff --git a/content/fetchers/file.h b/content/fetchers/file.h
deleted file mode 100644
index 5a5cfe8..0000000
--- a/content/fetchers/file.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2010 Vincent Sanders <vince(a)netsurf-browser.org>
- *
- * This file is part of NetSurf.
- *
- * NetSurf is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * NetSurf is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * \file
- * file scheme fetcher handler interface.
- */
-
-#ifndef NETSURF_CONTENT_FETCHERS_FETCH_FILE_H
-#define NETSURF_CONTENT_FETCHERS_FETCH_FILE_H
-
-/**
- * Register file scheme handler.
- *
- * \return NSERROR_OK on successful registration or error code on failure.
- */
-nserror fetch_file_register(void);
-
-#endif
diff --git a/content/fetchers/file/Makefile b/content/fetchers/file/Makefile
new file mode 100644
index 0000000..c22400a
--- /dev/null
+++ b/content/fetchers/file/Makefile
@@ -0,0 +1,3 @@
+# File fetcher sources
+
+S_FETCHER_FILE := dirlist.c file.c
diff --git a/content/fetchers/file/dirlist.c b/content/fetchers/file/dirlist.c
new file mode 100644
index 0000000..d49dc7f
--- /dev/null
+++ b/content/fetchers/file/dirlist.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2010 Michael Drake <tlsa(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Generate HTML content for displaying directory listings (implementation).
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "utils/nscolour.h"
+
+#include "netsurf/types.h"
+#include "netsurf/plot_style.h"
+
+#include "dirlist.h"
+#include "desktop/system_colour.h"
+
+static int dirlist_filesize_calculate(unsigned long *bytesize);
+static int dirlist_filesize_value(unsigned long bytesize);
+static char* dirlist_filesize_unit(unsigned long bytesize);
+
+
+/**
+ * Generates the top part of an HTML directory listing page
+ *
+ * \return Top of directory listing HTML
+ *
+ * This is part of a series of functions. To generate a complete page,
+ * call the following functions in order:
+ *
+ * dirlist_generate_top()
+ * dirlist_generate_hide_columns() -- optional
+ * dirlist_generate_title()
+ * dirlist_generate_parent_link() -- optional
+ * dirlist_generate_headings()
+ * dirlist_generate_row() -- call 'n' times for 'n' rows
+ * dirlist_generate_bottom()
+ */
+
+bool dirlist_generate_top(char *buffer, int buffer_length)
+{
+ int error = snprintf(buffer, buffer_length,
+ "<html>\n"
+ "<head>\n"
+ "<link rel=\"stylesheet\" title=\"Standard\" "
+ "type=\"text/css\" href=\"resource:internal.css\">\n"
+ "<style>\n");
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+
+}
+
+
+/**
+ * Generates the part of an HTML directory listing page that can suppress
+ * particular columns
+ *
+ * \param flags flags for which cols to suppress. 0 to suppress none
+ * \param buffer buffer to fill with generated HTML
+ * \param buffer_length maximum size of buffer
+ * \return true iff buffer filled without error
+ *
+ * This is part of a series of functions. To generate a complete page,
+ * call the following functions in order:
+ *
+ * dirlist_generate_top()
+ * dirlist_generate_hide_columns() -- optional
+ * dirlist_generate_title()
+ * dirlist_generate_parent_link() -- optional
+ * dirlist_generate_headings()
+ * dirlist_generate_row() -- call 'n' times for 'n' rows
+ * dirlist_generate_bottom()
+ */
+
+bool dirlist_generate_hide_columns(int flags, char *buffer, int buffer_length)
+{
+ int error = snprintf(buffer, buffer_length,
+ "%s\n%s\n%s\n%s\n%s\n",
+ (flags & DIRLIST_NO_NAME_COLUMN) ?
+ "span.name { display: none; }\n" : "",
+ (flags & DIRLIST_NO_TYPE_COLUMN) ?
+ "span.type { display: none; }\n" : "",
+ (flags & DIRLIST_NO_SIZE_COLUMN) ?
+ "span.size { display: none; }\n" : "",
+ (flags & DIRLIST_NO_DATE_COLUMN) ?
+ "span.date { display: none; }\n" : "",
+ (flags & DIRLIST_NO_TIME_COLUMN) ?
+ "span.time { display: none; }\n" : "");
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+}
+
+
+/**
+ * Generates the part of an HTML directory listing page that contains the title
+ *
+ * \param title title to use
+ * \param buffer buffer to fill with generated HTML
+ * \param buffer_length maximum size of buffer
+ * \return true iff buffer filled without error
+ *
+ * This is part of a series of functions. To generate a complete page,
+ * call the following functions in order:
+ *
+ * dirlist_generate_top()
+ * dirlist_generate_hide_columns() -- optional
+ * dirlist_generate_title()
+ * dirlist_generate_parent_link() -- optional
+ * dirlist_generate_headings()
+ * dirlist_generate_row() -- call 'n' times for 'n' rows
+ * dirlist_generate_bottom()
+ */
+
+bool dirlist_generate_title(const char *title, char *buffer, int buffer_length)
+{
+ const char *stylesheet;
+ nserror err;
+ int error;
+
+ if (title == NULL)
+ title = "";
+
+ err = nscolour_get_stylesheet(&stylesheet);
+ if (err != NSERROR_OK) {
+ return false;
+ }
+
+ error = snprintf(buffer, buffer_length,
+ "</style>\n"
+ "<title>%s</title>\n"
+ "<style>\n"
+ "html {\n"
+ "\tbackground-color: #%06x;\n"
+ "}\n"
+ "%s"
+ "</style>\n"
+ "</head>\n"
+ "<body id=\"dirlist\" class=\"ns-even-bg ns-even-fg ns-border\">\n"
+ "<h1 class=\"ns-border\">%s</h1>\n",
+ title,
+ colour_rb_swap(nscolours[NSCOLOUR_WIN_ODD_BG]),
+ stylesheet, title);
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+}
+
+
+/**
+ * Generates the part of an HTML directory listing page that links to the parent
+ * directory
+ *
+ * \param parent url of parent directory
+ * \param buffer buffer to fill with generated HTML
+ * \param buffer_length maximum size of buffer
+ * \return true iff buffer filled without error
+ *
+ * This is part of a series of functions. To generate a complete page,
+ * call the following functions in order:
+ *
+ * dirlist_generate_top()
+ * dirlist_generate_hide_columns() -- optional
+ * dirlist_generate_title()
+ * dirlist_generate_parent_link() -- optional
+ * dirlist_generate_headings()
+ * dirlist_generate_row() -- call 'n' times for 'n' rows
+ * dirlist_generate_bottom()
+ */
+
+bool dirlist_generate_parent_link(const char *parent, char *buffer,
+ int buffer_length)
+{
+ int error = snprintf(buffer, buffer_length,
+ "<p><a href=\"%s\">%s</a></p>",
+ parent, messages_get("FileParent"));
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+}
+
+
+/**
+ * Generates the part of an HTML directory listing page that displays the column
+ * headings
+ *
+ * \param buffer buffer to fill with generated HTML
+ * \param buffer_length maximum size of buffer
+ * \return true iff buffer filled without error
+ *
+ * This is part of a series of functions. To generate a complete page,
+ * call the following functions in order:
+ *
+ * dirlist_generate_top()
+ * dirlist_generate_hide_columns() -- optional
+ * dirlist_generate_title()
+ * dirlist_generate_parent_link() -- optional
+ * dirlist_generate_headings()
+ * dirlist_generate_row() -- call 'n' times for 'n' rows
+ * dirlist_generate_bottom()
+ */
+
+bool dirlist_generate_headings(char *buffer, int buffer_length)
+{
+ int error = snprintf(buffer, buffer_length,
+ "<div>\n"
+ "<strong>\n"
+ "\t<span class=\"name\">%s</span>\n"
+ "\t<span class=\"type\">%s</span>\n"
+ "\t<span class=\"size\">%s</span>"
+ "<span class=\"size\"></span>\n"
+ "\t<span class=\"date\">%s</span>\n"
+ "\t<span class=\"time\">%s</span>\n"
+ "</strong>\n",
+ messages_get("FileName"), messages_get("FileType"),
+ messages_get("FileSize"), messages_get("FileDate"),
+ messages_get("FileTime"));
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+}
+
+
+/**
+ * Generates the part of an HTML directory listing page that displays a row
+ * in the directory contents table
+ *
+ * \param even evenness of row number, for alternate row colouring
+ * \param directory whether this row is for a directory (or a file)
+ * \param url url for row entry
+ * \param name name of row entry
+ * \param mimetype MIME type of row entry
+ * \param size size of row entry. If negative, size is left blank
+ * \param date date row entry was last modified
+ * \param time time row entry was last modified
+ * \param buffer buffer to fill with generated HTML
+ * \param buffer_length maximum size of buffer
+ * \return true iff buffer filled without error
+ *
+ * This is part of a series of functions. To generate a complete page,
+ * call the following functions in order:
+ *
+ * dirlist_generate_top()
+ * dirlist_generate_hide_columns() -- optional
+ * dirlist_generate_title()
+ * dirlist_generate_parent_link() -- optional
+ * dirlist_generate_headings()
+ * dirlist_generate_row() -- call 'n' times for 'n' rows
+ * dirlist_generate_bottom()
+ */
+
+bool dirlist_generate_row(bool even, bool directory, nsurl *url, char *name,
+ const char *mimetype, long long size, char *date, char *time,
+ char *buffer, int buffer_length)
+{
+ const char *unit;
+ char size_string[100];
+ int error;
+
+ if (size < 0) {
+ unit = "";
+ strncpy(size_string, "", sizeof size_string);
+ } else {
+ unit = messages_get(dirlist_filesize_unit((unsigned long)size));
+ snprintf(size_string, sizeof size_string, "%d",
+ dirlist_filesize_value((unsigned long)size));
+ }
+
+ error = snprintf(buffer, buffer_length,
+ "<a href=\"%s\" class=\"%s %s\">\n"
+ "\t<span class=\"name ns-border\">%s</span>\n"
+ "\t<span class=\"type ns-border\">%s</span>\n"
+ "\t<span class=\"size ns-border\">%s</span>"
+ "<span class=\"size ns-border\">%s</span>\n"
+ "\t<span class=\"date ns-border\">%s</span>\n"
+ "\t<span class=\"time ns-border\">%s</span>\n"
+ "</a>\n", nsurl_access(url),
+ even ? "even ns-even-bg" : "odd ns-odd-bg",
+ directory ? "dir" : "file",
+ name, mimetype, size_string, unit, date, time);
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+}
+
+
+/**
+ * Generates the bottom part of an HTML directory listing page
+ *
+ * \return Bottom of directory listing HTML
+ *
+ * This is part of a series of functions. To generate a complete page,
+ * call the following functions in order:
+ *
+ * dirlist_generate_top()
+ * dirlist_generate_hide_columns() -- optional
+ * dirlist_generate_title()
+ * dirlist_generate_parent_link() -- optional
+ * dirlist_generate_headings()
+ * dirlist_generate_row() -- call 'n' times for 'n' rows
+ * dirlist_generate_bottom()
+ */
+
+bool dirlist_generate_bottom(char *buffer, int buffer_length)
+{
+ int error = snprintf(buffer, buffer_length,
+ "</div>\n"
+ "</body>\n"
+ "</html>\n");
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+}
+
+
+/**
+ * Obtain display value and units for filesize after conversion to B/kB/MB/GB,
+ * as appropriate.
+ *
+ * \param bytesize file size in bytes, updated to filesize in output units
+ * \return number of times bytesize has been divided by 1024
+ */
+
+int dirlist_filesize_calculate(unsigned long *bytesize)
+{
+ int i = 0;
+ while (*bytesize > 1024 * 4) {
+ *bytesize /= 1024;
+ i++;
+ if (i == 3)
+ break;
+ }
+ return i;
+}
+
+
+/**
+ * Obtain display value for filesize after conversion to B/kB/MB/GB,
+ * as appropriate
+ *
+ * \param bytesize file size in bytes
+ * \return Value to display for file size, in units given by filesize_unit()
+ */
+
+int dirlist_filesize_value(unsigned long bytesize)
+{
+ dirlist_filesize_calculate(&bytesize);
+ return (int)bytesize;
+}
+
+
+/**
+ * Obtain display units for filesize after conversion to B/kB/MB/GB,
+ * as appropriate
+ *
+ * \param bytesize file size in bytes
+ * \return Units to display for file size, for value given by filesize_value()
+ */
+
+char* dirlist_filesize_unit(unsigned long bytesize)
+{
+ const char* units[] = { "Bytes", "kBytes", "MBytes", "GBytes" };
+ return (char*)units[dirlist_filesize_calculate(&bytesize)];
+}
diff --git a/content/fetchers/file/dirlist.h b/content/fetchers/file/dirlist.h
new file mode 100644
index 0000000..3a0d48c
--- /dev/null
+++ b/content/fetchers/file/dirlist.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 Michael Drake <tlsa(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * interface to generate HTML content for displaying directory listings.
+ *
+ * These functions should in general be called via the content interface.
+ */
+
+#ifndef NETSURF_CONTENT_DIRLIST_H_
+#define NETSURF_CONTENT_DIRLIST_H_
+
+#include <stdbool.h>
+
+#define DIRLIST_NO_NAME_COLUMN 1
+#define DIRLIST_NO_TYPE_COLUMN 1 << 1
+#define DIRLIST_NO_SIZE_COLUMN 1 << 2
+#define DIRLIST_NO_DATE_COLUMN 1 << 3
+#define DIRLIST_NO_TIME_COLUMN 1 << 4
+
+struct nsurl;
+
+bool dirlist_generate_top(char *buffer, int buffer_length);
+bool dirlist_generate_hide_columns(int flags, char *buffer, int buffer_length);
+bool dirlist_generate_title(const char *title, char *buffer, int buffer_length);
+bool dirlist_generate_parent_link(const char *parent, char *buffer,
+ int buffer_length);
+bool dirlist_generate_headings(char *buffer, int buffer_length);
+bool dirlist_generate_row(bool even, bool directory, struct nsurl *url,
+ char *name, const char *mimetype, long long size, char *date,
+ char *time, char *buffer, int buffer_length);
+bool dirlist_generate_bottom(char *buffer, int buffer_length);
+
+#endif
diff --git a/content/fetchers/file/file.c b/content/fetchers/file/file.c
new file mode 100644
index 0000000..ff3a1b1
--- /dev/null
+++ b/content/fetchers/file/file.c
@@ -0,0 +1,844 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ *
+ * file scheme URL handling. Based on the data fetcher by Rob Kendrick
+ *
+ * output dates and directory ordering are affected by the current locale
+ */
+
+#include "utils/config.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+#include <libwapcaplet/libwapcaplet.h>
+
+#include "netsurf/inttypes.h"
+#include "utils/nsurl.h"
+#include "utils/dirent.h"
+#include "utils/corestrings.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/time.h"
+#include "utils/ring.h"
+#include "utils/file.h"
+#include "netsurf/fetch.h"
+#include "desktop/gui_internal.h"
+
+#include "content/fetch.h"
+#include "content/fetchers.h"
+#include "dirlist.h"
+#include "file.h"
+
+/* Maximum size of read buffer */
+#define FETCH_FILE_MAX_BUF_SIZE (1024 * 1024)
+
+/** Context for a fetch */
+struct fetch_file_context {
+ struct fetch_file_context *r_next, *r_prev;
+
+ struct fetch *fetchh; /**< Handle for this fetch */
+
+ bool aborted; /**< Flag indicating fetch has been aborted */
+ bool locked; /**< Flag indicating entry is already entered */
+
+ nsurl *url; /**< The full url the fetch refers to */
+ char *path; /**< The actual path to be used with open() */
+
+ time_t file_etag; /**< Request etag for file (previous st.m_time) */
+};
+
+static struct fetch_file_context *ring = NULL;
+
+/** issue fetch callbacks with locking */
+static inline bool fetch_file_send_callback(const fetch_msg *msg,
+ struct fetch_file_context *ctx)
+{
+ ctx->locked = true;
+ fetch_send_callback(msg, ctx->fetchh);
+ ctx->locked = false;
+
+ return ctx->aborted;
+}
+
+static bool fetch_file_send_header(struct fetch_file_context *ctx,
+ const char *fmt, ...)
+{
+ fetch_msg msg;
+ char header[64];
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = vsnprintf(header, sizeof header, fmt, ap);
+ va_end(ap);
+
+ if (len >= (int)sizeof(header) || len < 0) {
+ return false;
+ }
+
+ msg.type = FETCH_HEADER;
+ msg.data.header_or_data.buf = (const uint8_t *) header;
+ msg.data.header_or_data.len = len;
+
+ return fetch_file_send_callback(&msg, ctx);
+}
+
+/** callback to initialise the file fetcher. */
+static bool fetch_file_initialise(lwc_string *scheme)
+{
+ return true;
+}
+
+/** callback to initialise the file fetcher. */
+static void fetch_file_finalise(lwc_string *scheme)
+{
+}
+
+static bool fetch_file_can_fetch(const nsurl *url)
+{
+ return true;
+}
+
+/** callback to set up a file fetch context. */
+static void *
+fetch_file_setup(struct fetch *fetchh,
+ nsurl *url,
+ bool only_2xx,
+ bool downgrade_tls,
+ const char *post_urlenc,
+ const struct fetch_multipart_data *post_multipart,
+ const char **headers)
+{
+ struct fetch_file_context *ctx;
+ int i;
+ nserror ret;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ret = guit->file->nsurl_to_path(url, &ctx->path);
+ if (ret != NSERROR_OK) {
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->url = nsurl_ref(url);
+
+ /* Scan request headers looking for If-None-Match */
+ for (i = 0; headers[i] != NULL; i++) {
+ if (strncasecmp(headers[i], "If-None-Match:",
+ SLEN("If-None-Match:")) != 0) {
+ continue;
+ }
+
+ /* If-None-Match: "12345678" */
+ const char *d = headers[i] + SLEN("If-None-Match:");
+
+ /* Scan to first digit, if any */
+ while (*d != '\0' && (*d < '0' || '9' < *d))
+ d++;
+
+ /* Convert to time_t */
+ if (*d != '\0') {
+ ret = nsc_snptimet(d, strlen(d), &ctx->file_etag);
+ if (ret != NSERROR_OK) {
+ NSLOG(fetch, WARNING,
+ "Bad If-None-Match value");
+ }
+ }
+ }
+
+ ctx->fetchh = fetchh;
+
+ RING_INSERT(ring, ctx);
+
+ return ctx;
+}
+
+/** callback to free a file fetch */
+static void fetch_file_free(void *ctx)
+{
+ struct fetch_file_context *c = ctx;
+ nsurl_unref(c->url);
+ free(c->path);
+ free(ctx);
+}
+
+/** callback to start a file fetch */
+static bool fetch_file_start(void *ctx)
+{
+ return true;
+}
+
+/** callback to abort a file fetch */
+static void fetch_file_abort(void *ctx)
+{
+ struct fetch_file_context *c = ctx;
+
+ /* To avoid the poll loop having to deal with the fetch context
+ * disappearing from under it, we simply flag the abort here.
+ * The poll loop itself will perform the appropriate cleanup.
+ */
+ c->aborted = true;
+}
+
+static int fetch_file_errno_to_http_code(int error_no)
+{
+ switch (error_no) {
+ case ENAMETOOLONG:
+ return 400;
+ case EACCES:
+ return 403;
+ case ENOENT:
+ return 404;
+ default:
+ break;
+ }
+
+ return 500;
+}
+
+static void fetch_file_process_error(struct fetch_file_context *ctx, int code)
+{
+ fetch_msg msg;
+ char buffer[1024];
+ const char *title;
+ char key[8];
+
+ /* content is going to return error code */
+ fetch_set_http_code(ctx->fetchh, code);
+
+ /* content type */
+ if (fetch_file_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_file_process_error_aborted;
+
+ snprintf(key, sizeof key, "HTTP%03d", code);
+ title = messages_get(key);
+
+ snprintf(buffer, sizeof buffer, "<html><head><title>%s</title></head>"
+ "<body><h1>%s</h1>"
+ "<p>Error %d while fetching file %s</p></body></html>",
+ title, title, code, nsurl_access(ctx->url));
+
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = (const uint8_t *) buffer;
+ msg.data.header_or_data.len = strlen(buffer);
+ if (fetch_file_send_callback(&msg, ctx))
+ goto fetch_file_process_error_aborted;
+
+ msg.type = FETCH_FINISHED;
+ fetch_file_send_callback(&msg, ctx);
+
+fetch_file_process_error_aborted:
+ return;
+}
+
+
+/** Process object as a regular file */
+static void fetch_file_process_plain(struct fetch_file_context *ctx,
+ struct stat *fdstat)
+{
+#ifdef HAVE_MMAP
+ fetch_msg msg;
+ char *buf = NULL;
+ size_t buf_size;
+
+ int fd; /**< The file descriptor of the object */
+
+ /* Check if we can just return not modified */
+ if (ctx->file_etag != 0 && ctx->file_etag == fdstat->st_mtime) {
+ fetch_set_http_code(ctx->fetchh, 304);
+ msg.type = FETCH_NOTMODIFIED;
+ fetch_file_send_callback(&msg, ctx);
+ return;
+ }
+
+ fd = open(ctx->path, O_RDONLY);
+ if (fd < 0) {
+ /* process errors as appropriate */
+ fetch_file_process_error(ctx,
+ fetch_file_errno_to_http_code(errno));
+ return;
+ }
+
+ /* set buffer size */
+ buf_size = fdstat->st_size;
+
+ /* allocate the buffer storage */
+ if (buf_size > 0) {
+ buf = mmap(NULL, buf_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (buf == MAP_FAILED) {
+ msg.type = FETCH_ERROR;
+ msg.data.error = "Unable to map memory for file data buffer";
+ fetch_file_send_callback(&msg, ctx);
+ close(fd);
+ return;
+ }
+ }
+
+ /* fetch is going to be successful */
+ fetch_set_http_code(ctx->fetchh, 200);
+
+ /* Any callback can result in the fetch being aborted.
+ * Therefore, we _must_ check for this after _every_ call to
+ * fetch_file_send_callback().
+ */
+
+ /* content type */
+ if (fetch_file_send_header(ctx, "Content-Type: %s",
+ guit->fetch->filetype(ctx->path))) {
+ goto fetch_file_process_aborted;
+ }
+
+ /* content length */
+ if (fetch_file_send_header(ctx, "Content-Length: %" PRIsizet,
+ fdstat->st_size)) {
+ goto fetch_file_process_aborted;
+ }
+
+ /* create etag */
+ if (fetch_file_send_header(ctx, "ETag: \"%10" PRId64 "\"",
+ (int64_t) fdstat->st_mtime)) {
+ goto fetch_file_process_aborted;
+ }
+
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = (const uint8_t *) buf;
+ msg.data.header_or_data.len = buf_size;
+ fetch_file_send_callback(&msg, ctx);
+
+ if (ctx->aborted == false) {
+ msg.type = FETCH_FINISHED;
+ fetch_file_send_callback(&msg, ctx);
+ }
+
+fetch_file_process_aborted:
+
+ if (buf != NULL)
+ munmap(buf, buf_size);
+ close(fd);
+#else
+ fetch_msg msg;
+ char *buf;
+ size_t buf_size;
+
+ ssize_t tot_read = 0;
+ ssize_t res;
+
+ FILE *infile;
+
+ /* Check if we can just return not modified */
+ if (ctx->file_etag != 0 && ctx->file_etag == fdstat->st_mtime) {
+ fetch_set_http_code(ctx->fetchh, 304);
+ msg.type = FETCH_NOTMODIFIED;
+ fetch_file_send_callback(&msg, ctx);
+ return;
+ }
+
+ infile = fopen(ctx->path, "rb");
+ if (infile == NULL) {
+ /* process errors as appropriate */
+ fetch_file_process_error(ctx,
+ fetch_file_errno_to_http_code(errno));
+ return;
+ }
+
+ /* set buffer size */
+ buf_size = fdstat->st_size;
+ if (buf_size > FETCH_FILE_MAX_BUF_SIZE)
+ buf_size = FETCH_FILE_MAX_BUF_SIZE;
+
+ /* allocate the buffer storage */
+ buf = malloc(buf_size);
+ if (buf == NULL) {
+ msg.type = FETCH_ERROR;
+ msg.data.error =
+ "Unable to allocate memory for file data buffer";
+ fetch_file_send_callback(&msg, ctx);
+ fclose(infile);
+ return;
+ }
+
+ /* fetch is going to be successful */
+ fetch_set_http_code(ctx->fetchh, 200);
+
+ /* Any callback can result in the fetch being aborted.
+ * Therefore, we _must_ check for this after _every_ call to
+ * fetch_file_send_callback().
+ */
+
+ /* content type */
+ if (fetch_file_send_header(ctx, "Content-Type: %s",
+ guit->fetch->filetype(ctx->path))) {
+ goto fetch_file_process_aborted;
+ }
+
+ /* content length */
+ if (fetch_file_send_header(ctx, "Content-Length: %" PRIsizet,
+ fdstat->st_size)) {
+ goto fetch_file_process_aborted;
+ }
+
+ /* create etag */
+ if (fetch_file_send_header(ctx, "ETag: \"%10" PRId64 "\"",
+ (int64_t) fdstat->st_mtime)) {
+ goto fetch_file_process_aborted;
+ }
+
+ /* main data loop */
+ while (tot_read < fdstat->st_size) {
+ res = fread(buf, 1, buf_size, infile);
+ if (res == 0) {
+ if (feof(infile)) {
+ msg.type = FETCH_ERROR;
+ msg.data.error = "Unexpected EOF reading file";
+ fetch_file_send_callback(&msg, ctx);
+ goto fetch_file_process_aborted;
+ } else {
+ msg.type = FETCH_ERROR;
+ msg.data.error = "Error reading file";
+ fetch_file_send_callback(&msg, ctx);
+ goto fetch_file_process_aborted;
+ }
+ }
+ tot_read += res;
+
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = (const uint8_t *) buf;
+ msg.data.header_or_data.len = res;
+ if (fetch_file_send_callback(&msg, ctx))
+ break;
+ }
+
+ if (ctx->aborted == false) {
+ msg.type = FETCH_FINISHED;
+ fetch_file_send_callback(&msg, ctx);
+ }
+
+fetch_file_process_aborted:
+
+ fclose(infile);
+ free(buf);
+#endif
+ return;
+}
+
+static char *gen_nice_title(char *path)
+{
+ char *nice_path, *cnv, *tmp;
+ char *title;
+ int title_length;
+
+ /* Convert path for display */
+ nice_path = malloc(strlen(path) * SLEN("&") + 1);
+ if (nice_path == NULL) {
+ return NULL;
+ }
+
+ /* Escape special HTML characters */
+ for (cnv = nice_path, tmp = path; *tmp != '\0'; tmp++) {
+ if (*tmp == '<') {
+ *cnv++ = '&';
+ *cnv++ = 'l';
+ *cnv++ = 't';
+ *cnv++ = ';';
+ } else if (*tmp == '>') {
+ *cnv++ = '&';
+ *cnv++ = 'g';
+ *cnv++ = 't';
+ *cnv++ = ';';
+ } else if (*tmp == '&') {
+ *cnv++ = '&';
+ *cnv++ = 'a';
+ *cnv++ = 'm';
+ *cnv++ = 'p';
+ *cnv++ = ';';
+ } else {
+ *cnv++ = *tmp;
+ }
+ }
+ *cnv = '\0';
+
+ /* Construct a localised title string */
+ title_length = (cnv - nice_path) + strlen(messages_get("FileIndex"));
+ title = malloc(title_length + 1);
+
+ if (title == NULL) {
+ free(nice_path);
+ return NULL;
+ }
+
+ /* Set title to localised "Index of <nice_path>" */
+ snprintf(title, title_length, messages_get("FileIndex"), nice_path);
+
+ free(nice_path);
+
+ return title;
+}
+
+/**
+ * Generate an output row of the directory listing.
+ *
+ * \param ctx The file fetching context.
+ * \param ent current directory entry.
+ * \param even is the row an even row.
+ * \param buffer The output buffer.
+ * \param buffer_len The space available in the output buffer.
+ * \return NSERROR_OK or error code on faliure.
+ */
+static nserror
+process_dir_ent(struct fetch_file_context *ctx,
+ struct dirent *ent,
+ bool even,
+ char *buffer,
+ size_t buffer_len)
+{
+ nserror ret;
+ char *urlpath = NULL; /* buffer for leaf entry path */
+ struct stat ent_stat; /* stat result of leaf entry */
+ char datebuf[64]; /* buffer for date text */
+ char timebuf[64]; /* buffer for time text */
+ nsurl *url;
+
+ /* skip hidden files */
+ if (ent->d_name[0] == '.') {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ ret = netsurf_mkpath(&urlpath, NULL, 2, ctx->path, ent->d_name);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ if (stat(urlpath, &ent_stat) != 0) {
+ ent_stat.st_mode = 0;
+ datebuf[0] = 0;
+ timebuf[0] = 0;
+ } else {
+ /* Get date in output format. a (day of week) and b
+ * (month) are both affected by the locale
+ */
+ if (strftime((char *)&datebuf, sizeof datebuf, "%a %d %b %Y",
+ localtime(&ent_stat.st_mtime)) == 0) {
+ datebuf[0] = '-';
+ datebuf[1] = 0;
+ }
+
+ /* Get time in output format */
+ if (strftime((char *)&timebuf, sizeof timebuf, "%H:%M",
+ localtime(&ent_stat.st_mtime)) == 0) {
+ timebuf[0] = '-';
+ timebuf[1] = 0;
+ }
+ }
+
+ ret = guit->file->path_to_nsurl(urlpath, &url);
+ if (ret != NSERROR_OK) {
+ free(urlpath);
+ return ret;
+ }
+
+ if (S_ISREG(ent_stat.st_mode)) {
+ /* regular file */
+ dirlist_generate_row(even,
+ false,
+ url,
+ ent->d_name,
+ guit->fetch->filetype(urlpath),
+ ent_stat.st_size,
+ datebuf, timebuf,
+ buffer, buffer_len);
+ } else if (S_ISDIR(ent_stat.st_mode)) {
+ /* directory */
+ dirlist_generate_row(even,
+ true,
+ url,
+ ent->d_name,
+ messages_get("FileDirectory"),
+ -1,
+ datebuf, timebuf,
+ buffer, buffer_len);
+ } else {
+ /* something else */
+ dirlist_generate_row(even,
+ false,
+ url,
+ ent->d_name,
+ "",
+ -1,
+ datebuf, timebuf,
+ buffer, buffer_len);
+ }
+
+ nsurl_unref(url);
+ free(urlpath);
+
+ return NSERROR_OK;
+}
+
+/**
+ * Comparison function for sorting directories.
+ *
+ * Correctly orders non zero-padded numerical parts.
+ * ie. produces "file1, file2, file10" rather than "file1, file10, file2".
+ *
+ * \param d1 first directory entry
+ * \param d2 second directory entry
+ */
+static int dir_sort_alpha(const struct dirent **d1, const struct dirent **d2)
+{
+ const char *s1 = (*d1)->d_name;
+ const char *s2 = (*d2)->d_name;
+
+ while (*s1 != '\0' && *s2 != '\0') {
+ if ((*s1 >= '0' && *s1 <= '9') &&
+ (*s2 >= '0' && *s2 <= '9')) {
+ int n1 = 0, n2 = 0;
+ while (*s1 >= '0' && *s1 <= '9') {
+ n1 = n1 * 10 + (*s1) - '0';
+ s1++;
+ }
+ while (*s2 >= '0' && *s2 <= '9') {
+ n2 = n2 * 10 + (*s2) - '0';
+ s2++;
+ }
+ if (n1 != n2) {
+ return n1 - n2;
+ }
+ if (*s1 == '\0' || *s2 == '\0')
+ break;
+ }
+ if (tolower(*s1) != tolower(*s2))
+ break;
+
+ s1++;
+ s2++;
+ }
+
+ return tolower(*s1) - tolower(*s2);
+}
+
+static void fetch_file_process_dir(struct fetch_file_context *ctx,
+ struct stat *fdstat)
+{
+ fetch_msg msg;
+ char buffer[1024]; /* Output buffer */
+ bool even = false; /* formatting flag */
+ char *title; /* pretty printed title */
+ nserror err; /* result from url routines */
+ nsurl *up; /* url of parent */
+
+ struct dirent **listing = NULL; /* directory entry listing */
+ int i; /* directory entry index */
+ int n; /* number of directory entries */
+
+ n = scandir(ctx->path, &listing, 0, dir_sort_alpha);
+ if (n < 0) {
+ fetch_file_process_error(ctx,
+ fetch_file_errno_to_http_code(errno));
+ return;
+ }
+
+ /* fetch is going to be successful */
+ fetch_set_http_code(ctx->fetchh, 200);
+
+ /* force no-cache */
+ if (fetch_file_send_header(ctx, "Cache-Control: no-cache"))
+ goto fetch_file_process_dir_aborted;
+
+ /* content type */
+ if (fetch_file_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_file_process_dir_aborted;
+
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = (const uint8_t *) buffer;
+
+ /* directory listing top */
+ dirlist_generate_top(buffer, sizeof buffer);
+ msg.data.header_or_data.len = strlen(buffer);
+ if (fetch_file_send_callback(&msg, ctx))
+ goto fetch_file_process_dir_aborted;
+
+ /* directory listing title */
+ title = gen_nice_title(ctx->path);
+ dirlist_generate_title(title, buffer, sizeof buffer);
+ free(title);
+ msg.data.header_or_data.len = strlen(buffer);
+ if (fetch_file_send_callback(&msg, ctx))
+ goto fetch_file_process_dir_aborted;
+
+ /* Print parent directory link */
+ err = nsurl_parent(ctx->url, &up);
+ if (err == NSERROR_OK) {
+ if (nsurl_compare(ctx->url, up, NSURL_COMPLETE) == false) {
+ /* different URL; have parent */
+ dirlist_generate_parent_link(nsurl_access(up),
+ buffer, sizeof buffer);
+
+ msg.data.header_or_data.len = strlen(buffer);
+ fetch_file_send_callback(&msg, ctx);
+ }
+ nsurl_unref(up);
+
+ if (ctx->aborted)
+ goto fetch_file_process_dir_aborted;
+
+ }
+
+ /* directory list headings */
+ dirlist_generate_headings(buffer, sizeof buffer);
+ msg.data.header_or_data.len = strlen(buffer);
+ if (fetch_file_send_callback(&msg, ctx))
+ goto fetch_file_process_dir_aborted;
+
+ for (i = 0; i < n; i++) {
+
+ err = process_dir_ent(ctx, listing[i], even, buffer,
+ sizeof(buffer));
+
+ if (err == NSERROR_OK) {
+ msg.data.header_or_data.len = strlen(buffer);
+ if (fetch_file_send_callback(&msg, ctx))
+ goto fetch_file_process_dir_aborted;
+
+ even = !even;
+ }
+ }
+
+ /* directory listing bottom */
+ dirlist_generate_bottom(buffer, sizeof buffer);
+ msg.data.header_or_data.len = strlen(buffer);
+ if (fetch_file_send_callback(&msg, ctx))
+ goto fetch_file_process_dir_aborted;
+
+ msg.type = FETCH_FINISHED;
+ fetch_file_send_callback(&msg, ctx);
+
+fetch_file_process_dir_aborted:
+
+ if (listing != NULL) {
+ for (i = 0; i < n; i++) {
+ free(listing[i]);
+ }
+ free(listing);
+ }
+}
+
+
+/* process a file fetch */
+static void fetch_file_process(struct fetch_file_context *ctx)
+{
+ struct stat fdstat; /**< The objects stat */
+
+ if (stat(ctx->path, &fdstat) != 0) {
+ /* process errors as appropriate */
+ fetch_file_process_error(ctx,
+ fetch_file_errno_to_http_code(errno));
+ return;
+ }
+
+ if (S_ISDIR(fdstat.st_mode)) {
+ /* directory listing */
+ fetch_file_process_dir(ctx, &fdstat);
+ return;
+ } else if (S_ISREG(fdstat.st_mode)) {
+ /* regular file */
+ fetch_file_process_plain(ctx, &fdstat);
+ return;
+ } else {
+ /* unhandled type of file */
+ fetch_file_process_error(ctx, 501);
+ }
+
+ return;
+}
+
+/** callback to poll for additional file fetch contents */
+static void fetch_file_poll(lwc_string *scheme)
+{
+ struct fetch_file_context *c, *save_ring = NULL;
+
+ while (ring != NULL) {
+ /* Take the first entry from the ring */
+ c = ring;
+ RING_REMOVE(ring, c);
+
+ /* Ignore fetches that have been flagged as locked.
+ * This allows safe re-entrant calls to this function.
+ * Re-entrancy can occur if, as a result of a callback,
+ * the interested party causes fetch_poll() to be called
+ * again.
+ */
+ if (c->locked == true) {
+ RING_INSERT(save_ring, c);
+ continue;
+ }
+
+ /* Only process non-aborted fetches */
+ if (c->aborted == false) {
+ /* file fetches can be processed in one go */
+ fetch_file_process(c);
+ }
+
+ /* And now finish */
+ fetch_remove_from_queues(c->fetchh);
+ fetch_free(c->fetchh);
+
+ }
+
+ /* Finally, if we saved any fetches which were locked, put them back
+ * into the ring for next time
+ */
+ ring = save_ring;
+}
+
+nserror fetch_file_register(void)
+{
+ lwc_string *scheme = lwc_string_ref(corestring_lwc_file);
+ const struct fetcher_operation_table fetcher_ops = {
+ .initialise = fetch_file_initialise,
+ .acceptable = fetch_file_can_fetch,
+ .setup = fetch_file_setup,
+ .start = fetch_file_start,
+ .abort = fetch_file_abort,
+ .free = fetch_file_free,
+ .poll = fetch_file_poll,
+ .finalise = fetch_file_finalise
+ };
+
+ return fetcher_add(scheme, &fetcher_ops);
+}
diff --git a/content/fetchers/file/file.h b/content/fetchers/file/file.h
new file mode 100644
index 0000000..5a5cfe8
--- /dev/null
+++ b/content/fetchers/file/file.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * file scheme fetcher handler interface.
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_FETCH_FILE_H
+#define NETSURF_CONTENT_FETCHERS_FETCH_FILE_H
+
+/**
+ * Register file scheme handler.
+ *
+ * \return NSERROR_OK on successful registration or error code on failure.
+ */
+nserror fetch_file_register(void);
+
+#endif
-----------------------------------------------------------------------
Summary of changes:
content/Makefile | 7 ++++---
content/fetch.c | 2 +-
content/fetchers/Makefile | 9 +++++++--
content/fetchers/file/Makefile | 3 +++
content/{ => fetchers/file}/dirlist.c | 2 +-
content/{ => fetchers/file}/dirlist.h | 9 +++++----
content/fetchers/{ => file}/file.c | 4 ++--
content/fetchers/{ => file}/file.h | 0
8 files changed, 23 insertions(+), 13 deletions(-)
create mode 100644 content/fetchers/file/Makefile
rename content/{ => fetchers/file}/dirlist.c (99%)
rename content/{ => fetchers/file}/dirlist.h (91%)
rename content/fetchers/{ => file}/file.c (99%)
rename content/fetchers/{ => file}/file.h (100%)
diff --git a/content/Makefile b/content/Makefile
index abc5a24..188d0f4 100644
--- a/content/Makefile
+++ b/content/Makefile
@@ -3,7 +3,6 @@
S_CONTENT := \
content.c \
content_factory.c \
- dirlist.c \
fetch.c \
hlcache.c \
llcache.c \
@@ -18,10 +17,12 @@ ifeq ($(NETSURF_FS_BACKING_STORE),YES)
endif
-# Content fetchers sources
+# Content fetcher sources
include content/fetchers/Makefile
-# Content handlers
+S_FETCHERS := $(addprefix content/,$(S_FETCHERS))
+
+# Content handler sources
include content/handlers/Makefile
S_CONTENT := $(addprefix content/,$(S_CONTENT))
diff --git a/content/fetch.c b/content/fetch.c
index 4cc7859..a260799 100644
--- a/content/fetch.c
+++ b/content/fetch.c
@@ -56,7 +56,7 @@
#include "content/fetchers/about.h"
#include "content/fetchers/curl.h"
#include "content/fetchers/data.h"
-#include "content/fetchers/file.h"
+#include "content/fetchers/file/file.h"
#include "javascript/fetcher.h"
#include "content/urldb.h"
diff --git a/content/fetchers/Makefile b/content/fetchers/Makefile
index 9c84793..e87a4e8 100644
--- a/content/fetchers/Makefile
+++ b/content/fetchers/Makefile
@@ -1,10 +1,15 @@
# Content fetchers sources
-S_FETCHERS_YES := data.c file.c about.c resource.c
+S_FETCHERS_YES := data.c about.c resource.c
S_FETCHERS_NO :=
S_FETCHERS_$(NETSURF_USE_CURL) += curl.c
-S_FETCHERS := $(addprefix content/fetchers/,$(S_FETCHERS_YES))
+S_FETCHERS := $(addprefix fetchers/,$(S_FETCHERS_YES))
+
+# File fetcher
+include content/fetchers/file/Makefile
+
+S_FETCHERS += $(addprefix fetchers/file/,$(S_FETCHER_FILE))
# The following files depend on the testament
content/fetchers/about.c: testament $(OBJROOT)/testament.h
diff --git a/content/fetchers/file/Makefile b/content/fetchers/file/Makefile
new file mode 100644
index 0000000..c22400a
--- /dev/null
+++ b/content/fetchers/file/Makefile
@@ -0,0 +1,3 @@
+# File fetcher sources
+
+S_FETCHER_FILE := dirlist.c file.c
diff --git a/content/dirlist.c b/content/fetchers/file/dirlist.c
similarity index 99%
rename from content/dirlist.c
rename to content/fetchers/file/dirlist.c
index 3f79e65..d49dc7f 100644
--- a/content/dirlist.c
+++ b/content/fetchers/file/dirlist.c
@@ -32,7 +32,7 @@
#include "netsurf/types.h"
#include "netsurf/plot_style.h"
-#include "content/dirlist.h"
+#include "dirlist.h"
#include "desktop/system_colour.h"
static int dirlist_filesize_calculate(unsigned long *bytesize);
diff --git a/content/dirlist.h b/content/fetchers/file/dirlist.h
similarity index 91%
rename from content/dirlist.h
rename to content/fetchers/file/dirlist.h
index 5cdaf75..3a0d48c 100644
--- a/content/dirlist.h
+++ b/content/fetchers/file/dirlist.h
@@ -16,14 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/** \file
- * Generate HTML content for displaying directory listings (interface).
+/**
+ * \file
+ * interface to generate HTML content for displaying directory listings.
*
* These functions should in general be called via the content interface.
*/
-#ifndef _NETSURF_CONTENT_DIRLIST_H_
-#define _NETSURF_CONTENT_DIRLIST_H_
+#ifndef NETSURF_CONTENT_DIRLIST_H_
+#define NETSURF_CONTENT_DIRLIST_H_
#include <stdbool.h>
diff --git a/content/fetchers/file.c b/content/fetchers/file/file.c
similarity index 99%
rename from content/fetchers/file.c
rename to content/fetchers/file/file.c
index bf2cc32..ff3a1b1 100644
--- a/content/fetchers/file.c
+++ b/content/fetchers/file/file.c
@@ -57,10 +57,10 @@
#include "netsurf/fetch.h"
#include "desktop/gui_internal.h"
-#include "content/dirlist.h"
#include "content/fetch.h"
#include "content/fetchers.h"
-#include "content/fetchers/file.h"
+#include "dirlist.h"
+#include "file.h"
/* Maximum size of read buffer */
#define FETCH_FILE_MAX_BUF_SIZE (1024 * 1024)
diff --git a/content/fetchers/file.h b/content/fetchers/file/file.h
similarity index 100%
rename from content/fetchers/file.h
rename to content/fetchers/file/file.h
--
NetSurf Browser
2 years, 11 months