[gimp] app: port ABR brush loading to GIO
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: port ABR brush loading to GIO
- Date: Fri, 4 Jul 2014 00:21:40 +0000 (UTC)
commit e7d9e01d630074fbd85d97a3660bdd13d8f04a17
Author: Michael Natterer <mitch gimp org>
Date: Fri Jul 4 02:18:52 2014 +0200
app: port ABR brush loading to GIO
app/core/gimpbrush-load.c | 501 +++++++++++++++++++++++++++++----------------
1 files changed, 321 insertions(+), 180 deletions(-)
---
diff --git a/app/core/gimpbrush-load.c b/app/core/gimpbrush-load.c
index 7cc7097..0c3c34f 100644
--- a/app/core/gimpbrush-load.c
+++ b/app/core/gimpbrush-load.c
@@ -19,10 +19,7 @@
#include "config.h"
-#include <errno.h>
-
#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <glib/gstdio.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
@@ -70,38 +67,44 @@ struct _AbrSampledBrushHeader
/* local function prototypes */
-static GList * gimp_brush_load_abr_v12 (FILE *f,
- AbrHeader *abr_hdr,
- GFile *file,
- GError **error);
-static GList * gimp_brush_load_abr_v6 (FILE *f,
- AbrHeader *abr_hdr,
- GFile *file,
- GError **error);
-static GimpBrush * gimp_brush_load_abr_brush_v12 (FILE *f,
- AbrHeader *abr_hdr,
- gint index,
- GFile *file,
- GError **error);
-static GimpBrush * gimp_brush_load_abr_brush_v6 (FILE *f,
- AbrHeader *abr_hdr,
- gint32 max_offset,
- gint index,
- GFile *file,
- GError **error);
-
-static gchar abr_read_char (FILE *f);
-static gint16 abr_read_short (FILE *f);
-static gint32 abr_read_long (FILE *f);
-static gchar * abr_read_ucs2_text (FILE *f);
-static gboolean abr_supported (AbrHeader *abr_hdr,
- GFile *f,
- GError **error);
-static gboolean abr_reach_8bim_section (FILE *abr,
- const gchar *name);
-static gint32 abr_rle_decode (FILE *f,
- gchar *buffer,
- gint32 height);
+static GList * gimp_brush_load_abr_v12 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ GFile *file,
+ GError **error);
+static GList * gimp_brush_load_abr_v6 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ GFile *file,
+ GError **error);
+static GimpBrush * gimp_brush_load_abr_brush_v12 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ gint index,
+ GFile *file,
+ GError **error);
+static GimpBrush * gimp_brush_load_abr_brush_v6 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ gint32 max_offset,
+ gint index,
+ GFile *file,
+ GError **error);
+
+static gchar abr_read_char (GDataInputStream *input,
+ GError **error);
+static gint16 abr_read_short (GDataInputStream *input,
+ GError **error);
+static gint32 abr_read_long (GDataInputStream *input,
+ GError **error);
+static gchar * abr_read_ucs2_text (GDataInputStream *input,
+ GError **error);
+static gboolean abr_supported (AbrHeader *abr_hdr,
+ GFile *f,
+ GError **error);
+static gboolean abr_reach_8bim_section (GDataInputStream *input,
+ const gchar *name,
+ GError **error);
+static gboolean abr_rle_decode (GDataInputStream *input,
+ gchar *buffer,
+ gint32 height,
+ GError **error);
/* public functions */
@@ -428,55 +431,74 @@ gimp_brush_load_abr (GimpContext *context,
GFile *file,
GError **error)
{
- gchar *path;
- FILE *f;
- AbrHeader abr_hdr;
- GList *brush_list = NULL;
+ GInputStream *input;
+ GDataInputStream *data_input;
+ AbrHeader abr_hdr;
+ GList *brush_list = NULL;
+ GError *my_error = NULL;
g_return_val_if_fail (G_IS_FILE (file), NULL);
-
- path = g_file_get_path (file);
-
- g_return_val_if_fail (g_path_is_absolute (path), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
- f = g_fopen (path, "rb");
- g_free (path);
-
- if (! f)
+ input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error));
+ if (! input)
{
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN,
_("Could not open '%s' for reading: %s"),
- gimp_file_get_utf8_name (file), g_strerror (errno));
+ gimp_file_get_utf8_name (file), my_error->message);
+ g_clear_error (&my_error);
return NULL;
}
- abr_hdr.version = abr_read_short (f);
- abr_hdr.count = abr_read_short (f); /* sub-version for ABR v6 */
+ data_input = g_data_input_stream_new (input);
+ g_object_unref (input);
+
+ g_data_input_stream_set_byte_order (data_input,
+ G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
+
+ abr_hdr.version = abr_read_short (data_input, &my_error);
+ if (my_error)
+ goto done;
+
+ /* sub-version for ABR v6 */
+ abr_hdr.count = abr_read_short (data_input, &my_error);
+ if (my_error)
+ goto done;
- if (abr_supported (&abr_hdr, file, error))
+ if (abr_supported (&abr_hdr, file, &my_error))
{
switch (abr_hdr.version)
{
case 1:
case 2:
- brush_list = gimp_brush_load_abr_v12 (f, &abr_hdr,
- file, error);
+ brush_list = gimp_brush_load_abr_v12 (data_input, &abr_hdr,
+ file, &my_error);
break;
case 6:
- brush_list = gimp_brush_load_abr_v6 (f, &abr_hdr,
- file, error);
+ brush_list = gimp_brush_load_abr_v6 (data_input, &abr_hdr,
+ file, &my_error);
+ break;
}
}
- fclose (f);
+ done:
+
+ g_object_unref (data_input);
+
+ if (! brush_list)
+ {
+ if (! my_error)
+ g_set_error (&my_error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
+ _("Unable to decode abr format version %d."),
+ abr_hdr.version);
- if (! brush_list && (error && ! *error))
- g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
- _("Fatal parse error in brush file '%s': "
- "unable to decode abr format version %d."),
- gimp_file_get_utf8_name (file), abr_hdr.version);
+ g_set_error (error, my_error->domain, my_error->code,
+ _("Fatal parse error in brush file '%s': %s"),
+ gimp_file_get_utf8_name (file), my_error->message);
+ }
+
+ g_clear_error (&my_error);
return g_list_reverse (brush_list);
}
@@ -485,10 +507,10 @@ gimp_brush_load_abr (GimpContext *context,
/* private functions */
static GList *
-gimp_brush_load_abr_v12 (FILE *f,
- AbrHeader *abr_hdr,
- GFile *file,
- GError **error)
+gimp_brush_load_abr_v12 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ GFile *file,
+ GError **error)
{
GList *brush_list = NULL;
gint i;
@@ -498,7 +520,7 @@ gimp_brush_load_abr_v12 (FILE *f,
GimpBrush *brush;
GError *my_error = NULL;
- brush = gimp_brush_load_abr_brush_v12 (f, abr_hdr, i,
+ brush = gimp_brush_load_abr_brush_v12 (input, abr_hdr, i,
file, &my_error);
/* a NULL brush without an error means an unsupported brush
@@ -520,28 +542,32 @@ gimp_brush_load_abr_v12 (FILE *f,
}
static GList *
-gimp_brush_load_abr_v6 (FILE *f,
- AbrHeader *abr_hdr,
- GFile *file,
- GError **error)
+gimp_brush_load_abr_v6 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ GFile *file,
+ GError **error)
{
- GList *brush_list = NULL;
- gint32 sample_section_size;
- gint32 sample_section_end;
- gint i = 1;
+ GList *brush_list = NULL;
+ gint32 sample_section_size;
+ goffset sample_section_end;
+ gint i = 1;
+
+ if (! abr_reach_8bim_section (input, "samp", error))
+ return brush_list;
- if (! abr_reach_8bim_section (f, "samp"))
+ sample_section_size = abr_read_long (input, error);
+ if (error && *error)
return brush_list;
- sample_section_size = abr_read_long (f);
- sample_section_end = sample_section_size + ftell (f);
+ sample_section_end = (sample_section_size +
+ g_seekable_tell (G_SEEKABLE (input)));
- while (ftell (f) < sample_section_end)
+ while (g_seekable_tell (G_SEEKABLE (input)) < sample_section_end)
{
GimpBrush *brush;
GError *my_error = NULL;
- brush = gimp_brush_load_abr_brush_v6 (f, abr_hdr, sample_section_end,
+ brush = gimp_brush_load_abr_brush_v6 (input, abr_hdr, sample_section_end,
i, file, &my_error);
/* a NULL brush without an error means an unsupported brush
@@ -565,17 +591,22 @@ gimp_brush_load_abr_v6 (FILE *f,
}
static GimpBrush *
-gimp_brush_load_abr_brush_v12 (FILE *f,
- AbrHeader *abr_hdr,
- gint index,
- GFile *file,
- GError **error)
+gimp_brush_load_abr_brush_v12 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ gint index,
+ GFile *file,
+ GError **error)
{
GimpBrush *brush = NULL;
AbrBrushHeader abr_brush_hdr;
- abr_brush_hdr.type = abr_read_short (f);
- abr_brush_hdr.size = abr_read_long (f);
+ abr_brush_hdr.type = abr_read_short (input, error);
+ if (error && *error)
+ return NULL;
+
+ abr_brush_hdr.size = abr_read_long (input, error);
+ if (error && *error)
+ return NULL;
/* g_print(" + BRUSH\n | << type: %i block size: %i bytes\n",
* abr_brush_hdr.type, abr_brush_hdr.size);
@@ -591,7 +622,8 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
* types -akl
*/
g_printerr ("WARNING: computed brush unsupported, skipping.\n");
- fseek (f, abr_brush_hdr.size, SEEK_CUR);
+ g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
+ G_SEEK_CUR, NULL, NULL);
break;
case 2: /* sampled brush */
@@ -607,21 +639,42 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
gchar *tmp;
gshort compress;
- abr_sampled_brush_hdr.misc = abr_read_long (f);
- abr_sampled_brush_hdr.spacing = abr_read_short (f);
+ abr_sampled_brush_hdr.misc = abr_read_long (input, error);
+ if (error && *error)
+ break;
+
+ abr_sampled_brush_hdr.spacing = abr_read_short (input, error);
+ if (error && *error)
+ break;
if (abr_hdr->version == 2)
- sample_name = abr_read_ucs2_text (f);
+ {
+ sample_name = abr_read_ucs2_text (input, error);
+ if (error && *error)
+ break;
+ }
- abr_sampled_brush_hdr.antialiasing = abr_read_char (f);
+ abr_sampled_brush_hdr.antialiasing = abr_read_char (input, error);
+ if (error && *error)
+ break;
for (i = 0; i < 4; i++)
- abr_sampled_brush_hdr.bounds[i] = abr_read_short (f);
+ {
+ abr_sampled_brush_hdr.bounds[i] = abr_read_short (input, error);
+ if (error && *error)
+ break;
+ }
for (i = 0; i < 4; i++)
- abr_sampled_brush_hdr.bounds_long[i] = abr_read_long (f);
+ {
+ abr_sampled_brush_hdr.bounds_long[i] = abr_read_long (input, error);
+ if (error && *error)
+ break;
+ }
- abr_sampled_brush_hdr.depth = abr_read_short (f);
+ abr_sampled_brush_hdr.depth = abr_read_short (input, error);
+ if (error && *error)
+ break;
height = (abr_sampled_brush_hdr.bounds_long[2] -
abr_sampled_brush_hdr.bounds_long[0]); /* bottom - top */
@@ -641,7 +694,7 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
_("Fatal parse error in brush file '%s': "
"Wide brushes are not supported."),
gimp_file_get_utf8_name (file));
- return NULL;
+ break;
}
tmp = g_path_get_basename (gimp_file_get_utf8_name (file));
@@ -677,7 +730,13 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
mask = gimp_temp_buf_get_data (brush->mask);
size = width * height * bytes;
- compress = abr_read_char (f);
+ compress = abr_read_char (input, error);
+ if (error && *error)
+ {
+ g_object_unref (brush);
+ brush = NULL;
+ break;
+ }
/* g_print(" | << size: %dx%d %d bit (%d bytes) %s\n",
* width, height, abr_sampled_brush_hdr.depth, size,
@@ -685,15 +744,35 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
*/
if (! compress)
- fread (mask, size, 1, f);
+ {
+ gsize bytes_read;
+
+ if (! g_input_stream_read_all (G_INPUT_STREAM (input),
+ mask, size,
+ &bytes_read, NULL, error) ||
+ bytes_read != size)
+ {
+ g_object_unref (brush);
+ brush = NULL;
+ break;
+ }
+ }
else
- abr_rle_decode (f, (gchar *) mask, height);
+ {
+ if (! abr_rle_decode (input, (gchar *) mask, height, error))
+ {
+ g_object_unref (brush);
+ brush = NULL;
+ break;
+ }
+ }
}
break;
default:
g_printerr ("WARNING: unknown brush type, skipping.\n");
- fseek (f, abr_brush_hdr.size, SEEK_CUR);
+ g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
+ G_SEEK_CUR, NULL, NULL);
break;
}
@@ -701,19 +780,19 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
}
static GimpBrush *
-gimp_brush_load_abr_brush_v6 (FILE *f,
- AbrHeader *abr_hdr,
- gint32 max_offset,
- gint index,
- GFile *file,
- GError **error)
+gimp_brush_load_abr_brush_v6 (GDataInputStream *input,
+ AbrHeader *abr_hdr,
+ gint32 max_offset,
+ gint index,
+ GFile *file,
+ GError **error)
{
GimpBrush *brush = NULL;
guchar *mask;
gint32 brush_size;
gint32 brush_end;
- gint32 next_brush;
+ goffset next_brush;
gint32 top, left, bottom, right;
gint16 depth;
@@ -724,39 +803,48 @@ gimp_brush_load_abr_brush_v6 (FILE *f,
gchar *tmp;
gchar *name;
- gint r;
+ gboolean r;
+
+ brush_size = abr_read_long (input, error);
+ if (error && *error)
+ return NULL;
- brush_size = abr_read_long (f);
brush_end = brush_size;
/* complement to 4 */
while (brush_end % 4 != 0)
brush_end++;
- next_brush = ftell (f) + brush_end;
+ next_brush = (brush_end + g_seekable_tell (G_SEEKABLE (input)));
if (abr_hdr->count == 1)
- /* discard key and short coordinates and unknown short */
- r = fseek (f, 47, SEEK_CUR);
+ {
+ /* discard key and short coordinates and unknown short */
+ r = g_seekable_seek (G_SEEKABLE (input), 47, G_SEEK_CUR,
+ NULL, error);
+ }
else
- /* discard key and unknown bytes */
- r = fseek (f, 301, SEEK_CUR);
+ {
+ /* discard key and unknown bytes */
+ r = g_seekable_seek (G_SEEKABLE (input), 301, G_SEEK_CUR,
+ NULL, error);
+ }
- if (r == -1)
+ if (! r)
{
- g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
- _("Fatal parse error in brush file '%s': "
- "File appears truncated."),
- gimp_file_get_utf8_name (file));
+ g_prefix_error (error,
+ _("Fatal parse error in brush file '%s': "
+ "File appears truncated: "),
+ gimp_file_get_utf8_name (file));
return NULL;
}
- top = abr_read_long (f);
- left = abr_read_long (f);
- bottom = abr_read_long (f);
- right = abr_read_long (f);
- depth = abr_read_short (f);
- compress = abr_read_char (f);
+ top = abr_read_long (input, error); if (error && *error) return NULL;
+ left = abr_read_long (input, error); if (error && *error) return NULL;
+ bottom = abr_read_long (input, error); if (error && *error) return NULL;
+ right = abr_read_long (input, error); if (error && *error) return NULL;
+ depth = abr_read_short (input, error); if (error && *error) return NULL;
+ compress = abr_read_char (input, error); if (error && *error) return NULL;
width = right - left;
height = bottom - top;
@@ -786,44 +874,58 @@ gimp_brush_load_abr_brush_v6 (FILE *f,
/* data decoding */
if (! compress)
- /* not compressed - read raw bytes as brush data */
- fread (mask, size, 1, f);
+ {
+ /* not compressed - read raw bytes as brush data */
+ gsize bytes_read;
+
+ if (! g_input_stream_read_all (G_INPUT_STREAM (input),
+ mask, size,
+ &bytes_read, NULL, error) ||
+ bytes_read != size)
+ {
+ g_object_unref (brush);
+ return NULL;
+ }
+ }
else
- abr_rle_decode (f, (gchar *) mask, height);
+ {
+ if (! abr_rle_decode (input, (gchar *) mask, height, error))
+ {
+ g_object_unref (brush);
+ return NULL;
+ }
+ }
- fseek (f, next_brush, SEEK_SET);
+ g_seekable_seek (G_SEEKABLE (input), next_brush, G_SEEK_SET,
+ NULL, NULL);
return brush;
}
static gchar
-abr_read_char (FILE *f)
+abr_read_char (GDataInputStream *input,
+ GError **error)
{
- return fgetc (f);
+ return g_data_input_stream_read_byte (input, NULL, error);
}
static gint16
-abr_read_short (FILE *f)
+abr_read_short (GDataInputStream *input,
+ GError **error)
{
- gint16 val;
-
- fread (&val, sizeof (val), 1, f);
-
- return GINT16_FROM_BE (val);
+ return g_data_input_stream_read_int16 (input, NULL, error);
}
static gint32
-abr_read_long (FILE *f)
+abr_read_long (GDataInputStream *input,
+ GError **error)
{
- gint32 val;
-
- fread (&val, sizeof (val), 1, f);
-
- return GINT32_FROM_BE (val);
+ return g_data_input_stream_read_int32 (input, NULL, error);
}
static gchar *
-abr_read_ucs2_text (FILE *f)
+abr_read_ucs2_text (GDataInputStream *input,
+ GError **error)
{
gchar *name_ucs2;
gchar *name_utf8;
@@ -836,14 +938,21 @@ abr_read_ucs2_text (FILE *f)
* data : zero terminated UCS-2 string
*/
- len = 2 * abr_read_long (f);
+ len = 2 * abr_read_long (input, error);
if (len <= 0)
return NULL;
name_ucs2 = g_new (gchar, len);
for (i = 0; i < len; i++)
- name_ucs2[i] = abr_read_char (f);
+ {
+ name_ucs2[i] = abr_read_char (input, error);
+ if (error && *error)
+ {
+ g_free (name_ucs2);
+ return NULL;
+ }
+ }
name_utf8 = g_convert (name_ucs2, len,
"UTF-8", "UCS-2BE",
@@ -871,17 +980,16 @@ abr_supported (AbrHeader *abr_hdr,
if (abr_hdr->count == 1 || abr_hdr->count == 2)
return TRUE;
- if (error && ! (*error))
- g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
- _("Fatal parse error in brush file '%s': "
- "unable to decode abr format version %d."),
- gimp_file_get_utf8_name (file),
-
- /* horrid subversion display, but better than
- * having yet another translatable string for
- * this
- */
- abr_hdr->version * 10 + abr_hdr->count);
+ g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
+ _("Fatal parse error in brush file '%s': "
+ "unable to decode abr format version %d."),
+ gimp_file_get_utf8_name (file),
+
+ /* horrid subversion display, but better than
+ * having yet another translatable string for
+ * this
+ */
+ abr_hdr->version * 10 + abr_hdr->count);
break;
}
@@ -889,25 +997,30 @@ abr_supported (AbrHeader *abr_hdr,
}
static gboolean
-abr_reach_8bim_section (FILE *abr,
- const gchar *name)
+abr_reach_8bim_section (GDataInputStream *input,
+ const gchar *name,
+ GError **error)
{
- gchar tag[4];
- gchar tagname[5];
- gint32 section_size;
- gint r;
-
- while (! feof (abr))
+ while (TRUE)
{
- r = fread (&tag, 1, 4, abr);
- if (r != 4)
+ gchar tag[4];
+ gchar tagname[5];
+ gint32 section_size;
+ gsize bytes_read;
+
+ if (! g_input_stream_read_all (G_INPUT_STREAM (input),
+ tag, 4,
+ &bytes_read, NULL, error) ||
+ bytes_read != 4)
return FALSE;
if (strncmp (tag, "8BIM", 4))
return FALSE;
- r = fread (&tagname, 1, 4, abr);
- if (r != 4)
+ if (! g_input_stream_read_all (G_INPUT_STREAM (input),
+ tagname, 4,
+ &bytes_read, NULL, error) ||
+ bytes_read != 4)
return FALSE;
tagname[4] = '\0';
@@ -915,19 +1028,23 @@ abr_reach_8bim_section (FILE *abr,
if (! strncmp (tagname, name, 4))
return TRUE;
- section_size = abr_read_long (abr);
- r = fseek (abr, section_size, SEEK_CUR);
- if (r == -1)
+ section_size = abr_read_long (input, error);
+ if (error && *error)
+ return FALSE;
+
+ if (! g_seekable_seek (G_SEEKABLE (input), section_size, G_SEEK_CUR,
+ NULL, error))
return FALSE;
}
return FALSE;
}
-static gint32
-abr_rle_decode (FILE *f,
- gchar *buffer,
- gint32 height)
+static gboolean
+abr_rle_decode (GDataInputStream *input,
+ gchar *buffer,
+ gint32 height,
+ GError **error)
{
gchar ch;
gint i, j, c;
@@ -937,14 +1054,26 @@ abr_rle_decode (FILE *f,
/* read compressed size foreach scanline */
cscanline_len = g_new0 (gshort, height);
for (i = 0; i < height; i++)
- cscanline_len[i] = abr_read_short (f);
+ {
+ cscanline_len[i] = abr_read_short (input, error);
+ if (error && *error)
+ {
+ g_free (cscanline_len);
+ return FALSE;
+ }
+ }
/* unpack each scanline data */
for (i = 0; i < height; i++)
{
for (j = 0; j < cscanline_len[i];)
{
- gint32 n = abr_read_char (f);
+ gint32 n = abr_read_char (input, error);
+ if (error && *error)
+ {
+ g_free (cscanline_len);
+ return FALSE;
+ }
j++;
@@ -959,7 +1088,12 @@ abr_rle_decode (FILE *f,
continue;
n = -n + 1;
- ch = abr_read_char (f);
+ ch = abr_read_char (input, error);
+ if (error && *error)
+ {
+ g_free (cscanline_len);
+ return FALSE;
+ }
j++;
for (c = 0; c < n; c++, data++)
@@ -970,12 +1104,19 @@ abr_rle_decode (FILE *f,
/* read the following n + 1 chars (no compr) */
for (c = 0; c < n + 1; c++, j++, data++)
- *data = abr_read_char (f);
+ {
+ *data = abr_read_char (input, error);
+ if (error && *error)
+ {
+ g_free (cscanline_len);
+ return FALSE;
+ }
+ }
}
}
}
g_free (cscanline_len);
- return 0;
+ return TRUE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]