[krb5-auth-dialog] Add meson build system
- From: Guido Günther <guidog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [krb5-auth-dialog] Add meson build system
- Date: Tue, 9 Feb 2021 12:04:54 +0000 (UTC)
commit 95cce0b7d4e9e144b0367940f72620dfce1ed10a
Author: Guido Günther <agx sigxcpu org>
Date: Sat Feb 6 14:57:56 2021 +0100
Add meson build system
.gitignore | 1 +
.gitlab-ci.yml | 7 +-
build-aux/post_install.py | 15 +++
etpo/meson.build | 24 +++++
etpo/update-etpo.in | 18 ++++
help/meson.build | 20 ++++
icons/22x22/meson.build | 11 ++
icons/48x48/meson.build | 11 ++
icons/meson.build | 3 +
icons/scalable/meson.build | 9 ++
meson.build | 260 +++++++++++++++++++++++++++++++++++++++++++++
meson_options.txt | 23 ++++
plugins/meson.build | 50 +++++++++
po/meson.build | 2 +
src/meson.build | 101 ++++++++++++++++++
15 files changed, 551 insertions(+), 4 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 82aa0b7..7d8e1e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,4 @@ help/*/*.stamp
help/*/legal.xml
src/resources.c
src/krb5-auth-dialog.appdata.xml
+_build/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 208c3c9..cacb8fa 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,5 @@
variables:
DEBIAN_DEPS:
- autoconf
- automake
build-essential
bison
flex
@@ -13,6 +11,7 @@ variables:
libgtk-3-dev
libkrb5-dev
libpam0g-dev
+ meson
pkg-config
yelp-tools
@@ -23,5 +22,5 @@ build-debian:
- apt-get update
- apt-get -y install $DEBIAN_DEPS
script:
- - ./autogen.sh
- - make
+ - meson . _build
+ - ninja -C _build
diff --git a/build-aux/post_install.py b/build-aux/post_install.py
new file mode 100755
index 0000000..75222d6
--- /dev/null
+++ b/build-aux/post_install.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python3
+
+import os
+import subprocess
+import sys
+
+destdir = os.environ.get('DESTDIR', '')
+
+if not destdir and len(sys.argv) > 1:
+ datadir = sys.argv[1]
+
+ print('Compiling gsettings schemas...')
+ subprocess.call(['glib-compile-schemas', os.path.join(datadir, 'glib-2.0', 'schemas')])
+ print('Updating icon cache...')
+ subprocess.call(['gtk-update-icon-cache', '-qtf', os.path.join(datadir, 'icons', 'hicolor')])
diff --git a/etpo/meson.build b/etpo/meson.build
new file mode 100644
index 0000000..a2cc2e5
--- /dev/null
+++ b/etpo/meson.build
@@ -0,0 +1,24 @@
+if build_etpo
+
+ run_data = configuration_data()
+ run_data.set('ABS_BUILDDIR', meson.current_build_dir())
+ run_data.set('ABS_TOP_SRCDIR', meson.source_root())
+ configure_file(
+ input: 'update-etpo.in',
+ output: 'update-etpo',
+ configuration: run_data)
+
+ lgen = generator(flex,
+ output : '@PLAINNAME@.yy.c',
+ arguments : ['-o', '@OUTPUT@', '@INPUT@'])
+
+ ggen = generator(bison,
+ output : ['@BASENAME@.c', '@BASENAME@.h'],
+ arguments : ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'])
+
+ lfiles = lgen.process('lexer.l')
+ gfiles = ggen.process('grammar.y')
+
+ executable('etpo', lfiles, gfiles,
+ dependencies: glib_dep)
+endif
diff --git a/etpo/update-etpo.in b/etpo/update-etpo.in
new file mode 100755
index 0000000..d65a987
--- /dev/null
+++ b/etpo/update-etpo.in
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+set -e
+
+OUT=@ABS_TOP_SRCDIR@/src/dummy-strings.c
+# rebuild dummy strings for translation from Kerberos sources
+ET_SUBDIR="src/lib/krb5/"
+# ignore these to reduce translatable strings
+ET_IGNORE="(kdb5_err|krb524|KRB5PLACEHOLD)"
+
+if [ -z "${ET_DIR}" ]; then
+ echo "Need to set ET_DIR to Kerberos sources"
+ exit 1
+fi
+
+echo '/* Generated by hand via "cd etpo; make update-etpo" from ${ET_DIR} */' > "${OUT}".tmp
+@ABS_BUILDDIR@/etpo ${ET_DIR}/${ET_SUBDIR} | egrep -v "${ET_IGNORE}" >> "${OUT}".tmp
+mv -f "${OUT}".tmp "${OUT}"
diff --git a/help/meson.build b/help/meson.build
new file mode 100644
index 0000000..c0794e7
--- /dev/null
+++ b/help/meson.build
@@ -0,0 +1,20 @@
+krb5_auth_dialog_help_sources = [
+ 'index.docbook',
+ 'legal.xml',
+]
+
+krb5_auth_dialog_media = [
+ 'figures/ka-valid.png',
+ 'figures/ka-expired.png',
+ 'figures/ka-expiring.png',
+ 'figures/trayicon-valid.png',
+ 'figures/trayicon-expired.png',
+ 'figures/trayicon-expiring.png',
+]
+
+gnome.yelp(
+ 'krb5-auth-dialog',
+ media: krb5_auth_dialog_media,
+ sources: krb5_auth_dialog_help_sources,
+ languages: ['cs', 'de', 'el', 'es', 'pl', 'sl', 'sv', 'uk'],
+)
diff --git a/icons/22x22/meson.build b/icons/22x22/meson.build
new file mode 100644
index 0000000..2c42155
--- /dev/null
+++ b/icons/22x22/meson.build
@@ -0,0 +1,11 @@
+icons = [
+ 'krb-expiring-ticket.png',
+ 'krb-valid-ticket.png',
+ 'krb-no-valid-ticket.png',
+]
+
+install_data(
+ icons,
+ install_dir: 'share/icons/hicolor/22x22/status',
+)
+
diff --git a/icons/48x48/meson.build b/icons/48x48/meson.build
new file mode 100644
index 0000000..52fcd88
--- /dev/null
+++ b/icons/48x48/meson.build
@@ -0,0 +1,11 @@
+icons = [
+ 'krb-expiring-ticket.png',
+ 'krb-valid-ticket.png',
+ 'krb-no-valid-ticket.png',
+]
+
+install_data(
+ icons,
+ install_dir: 'share/icons/hicolor/48x48/status/',
+)
+
diff --git a/icons/meson.build b/icons/meson.build
new file mode 100644
index 0000000..b5a5cb2
--- /dev/null
+++ b/icons/meson.build
@@ -0,0 +1,3 @@
+subdir('scalable')
+subdir('22x22')
+subdir('48x48')
diff --git a/icons/scalable/meson.build b/icons/scalable/meson.build
new file mode 100644
index 0000000..a5fbc20
--- /dev/null
+++ b/icons/scalable/meson.build
@@ -0,0 +1,9 @@
+icons = [
+ 'krb-expiring-ticket.svg',
+ 'krb-valid-ticket.svg',
+ 'krb-no-valid-ticket.svg',
+]
+install_data(
+ icons,
+ install_dir: 'share/icons/hicolor/scalable/status/',
+)
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..f0df2f8
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,260 @@
+project('krb5-auth-dialog', 'c',
+ version: '3.26.1',
+ license: 'GPLv2-or-later',
+ meson_version: '>= 0.53.0',
+ default_options: [ 'warning_level=1', 'buildtype=debugoptimized', 'c_std=gnu11' ],
+)
+
+prefix = get_option('prefix')
+bindir = join_paths(prefix, get_option('bindir'))
+datadir = join_paths(prefix, get_option('datadir'))
+localedir = join_paths(prefix, get_option('localedir'))
+libdir = join_paths(prefix, get_option('libdir'))
+desktopdir = join_paths(datadir, 'applications')
+pluginsdir = join_paths(libdir, 'krb5-auth-dialog/plugins')
+
+glib_ver = 'GLIB_VERSION_2_58'
+
+add_project_arguments([
+ '-DHAVE_CONFIG_H',
+ '-DGLIB_VERSION_MIN_REQUIRED=@0@'.format(glib_ver),
+ '-DG_LOG_DOMAIN="KrbAuthDialog"',
+ '-I' + meson.build_root(),
+], language: 'c')
+
+root_inc = include_directories('.')
+src_inc = include_directories('src')
+
+cc = meson.get_compiler('c')
+
+global_c_args = []
+test_c_args = [
+ '-Wcast-align',
+ '-Wdate-time',
+ '-Wdeclaration-after-statement',
+ ['-Werror=format-security', '-Werror=format=2'],
+ '-Wendif-labels',
+ '-Werror=incompatible-pointer-types',
+ '-Werror=missing-declarations',
+ '-Werror=overflow',
+ '-Werror=return-type',
+ '-Werror=shift-count-overflow',
+ '-Werror=shift-overflow=2',
+ '-Werror=implicit-fallthrough=3',
+ '-Wfloat-equal',
+ '-Wformat-nonliteral',
+ '-Wformat-security',
+ '-Winit-self',
+ '-Wmaybe-uninitialized',
+ '-Wmissing-field-initializers',
+ '-Wmissing-include-dirs',
+ '-Wmissing-noreturn',
+ '-Wnested-externs',
+ '-Wno-missing-field-initializers',
+ '-Wno-sign-compare',
+ '-Wno-strict-aliasing',
+ '-Wno-unused-parameter',
+ '-Wold-style-definition',
+ '-Wpointer-arith',
+ '-Wredundant-decls',
+ '-Wshadow',
+ '-Wstrict-prototypes',
+ '-Wswitch-default',
+ '-Wswitch-enum',
+ '-Wtype-limits',
+ '-Wundef',
+ '-Wunused-function',
+]
+if get_option('buildtype') != 'plain'
+ test_c_args += '-fstack-protector-strong'
+endif
+
+foreach arg: test_c_args
+ if cc.has_multi_arguments(arg)
+ global_c_args += arg
+ endif
+endforeach
+add_project_arguments(
+ global_c_args,
+ language: 'c'
+)
+
+gnome = import('gnome')
+i18n = import('i18n')
+
+glib_dep = dependency('glib-2.0', version: '>=2.58')
+gio_dep = dependency('gio-2.0', version: '>=2.58')
+gtk_dep = dependency('gtk+-3.0', version: '>=3.14')
+gobject_dep = dependency('gobject-2.0', version: '>=2.50.0')
+gmodule_dep = dependency('gmodule-2.0')
+gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
+
+meson.add_install_script(
+ join_paths('build-aux', 'post_install.py'),
+ datadir
+)
+
+# Both heimdal and mit ship a krb5.pc:
+krb5_dep = dependency('krb5')
+
+conf_data = configuration_data()
+conf_data.set_quoted('PACKAGE', meson.project_name())
+conf_data.set_quoted('VERSION', meson.project_version())
+conf_data.set_quoted('PACKAGE_VERSION', meson.project_version())
+conf_data.set('CREDENTIAL_CHECK_INTERVAL', get_option('check-interval'))
+conf_data.set('MINUTES_BEFORE_PROMPTING', get_option('minimum-lifetime'))
+conf_data.set_quoted('SC_PKCS11', get_option('pkcs11'))
+conf_data.set_quoted('GETTEXT_PACKAGE', meson.project_name())
+conf_data.set_quoted('LOCALE_DIR', localedir)
+conf_data.set_quoted('KA_PLUGINS_DIR', pluginsdir)
+conf_data.set_quoted('DATA_DIR', datadir)
+
+# Hooray to different Kerberos implementations:
+if cc.has_member('krb5_creds', 'ticket_flags',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ # MIT
+ conf_data.set('HAVE_KRB5_CREDS_TICKET_FLAGS', 1)
+elif cc.has_member('krb5_creds', 'flags',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_CREDS_FLAGS', 1)
+
+ if cc.has_member('krb5_creds', 'flags.b.forwardable',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_CREDS_FLAGS_B_FORWARDABLE', 1)
+ endif
+
+ if cc.has_member('krb5_creds', 'flags.b.renewable',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_CREDS_FLAGS_B_RENEWABLE', 1)
+ endif
+
+ if cc.has_member('krb5_creds', 'flags.b.proxiable',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_CREDS_FLAGS_B_PROXIABLE', 1)
+ endif
+endif
+
+if cc.compiles('''
+ #include <krb5.h>
+ int main(int argc, char **argv)
+ {
+ static krb5_principal foo;
+ static krb5_data bar;
+ foo->realm = bar;
+ return 0;
+ }''',
+ name: 'Realm as data',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_PRINCIPAL_REALM_AS_DATA', 1)
+elif cc.compiles('''
+ #include <krb5.h>
+ #include <string.h>
+ int main(int argc, char **argv)
+ {
+ static krb5_principal foo;
+ return strlen(foo->realm);
+ }''',
+ name: 'Realm as string',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_PRINCIPAL_REALM_AS_STRING', 1)
+endif
+
+if cc.has_function('krb5_get_error_message',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_GET_ERROR_MESSAGE', 1)
+endif
+
+if cc.has_function('krb5_free_error_message',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_FREE_ERROR_MESSAGE', 1)
+endif
+
+if cc.has_function('krb5_free_error_string',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_FREE_ERROR_STRING', 1)
+endif
+
+if cc.has_function('krb5_get_renewed_creds',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_GET_RENEWED_CREDS', 1)
+endif
+
+if cc.has_function('krb5_get_init_creds_opt_set_default_flags',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS', 1)
+endif
+
+if cc.has_function('krb5_cc_clear_mcred',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set('HAVE_KRB5_CC_CLEAR_MCRED', 1)
+endif
+
+if cc.has_header('hx509_err.h',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set10('HAVE_HX509_ERR_H', 1)
+endif
+
+have_pkinit = false
+conf_data.set10('HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT', 0)
+if cc.has_function('krb5_get_init_creds_opt_set_pkinit',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set10('HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT', 1)
+ have_pkinit = true
+endif
+
+conf_data.set10('HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PA', 0)
+if cc.has_function('krb5_get_init_creds_opt_set_pa',
+ prefix: '''#include <krb5.h>''',
+ dependencies: krb5_dep)
+ conf_data.set10('HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PA', 1)
+ have_pkinit = true
+endif
+
+want_pkinit = get_option('pkinit')
+if not want_pkinit.disabled()
+ if have_pkinit == true
+ conf_data.set('ENABLE_PKINIT', 1)
+ elif want_pkinit.enabled()
+ error('Neither Heimdal nor MIT pkinit available')
+ endif
+endif
+
+want_pam_plugin = get_option('pam-plugin')
+pam_dep = cc.find_library('pam', required: want_pam_plugin)
+build_pam_plugin = pam_dep.found() and not want_pam_plugin.disabled()
+
+configure_file = configure_file(configuration: conf_data, output: 'config.h')
+
+want_etpo = get_option('etpo')
+flex = find_program('flex', required: want_etpo)
+bison = find_program('bison', required: want_etpo)
+build_etpo = flex.found() and bison.found()
+
+subdir('icons')
+subdir('po')
+subdir('src')
+subdir('plugins')
+subdir('help')
+subdir('etpo')
+
+summary({
+ 'Minimum Lifetime': conf_data.get('MINUTES_BEFORE_PROMPTING'),
+ 'Check Interval': conf_data.get('CREDENTIAL_CHECK_INTERVAL'),
+ 'pkinit': conf_data.get('ENABLE_PKINIT', 0),
+ 'pkcs11': conf_data.get('SC_PKCS11'),
+ 'pam-plugin': build_pam_plugin,
+ 'etpo': build_etpo,
+})
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..6b9ff13
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,23 @@
+option('check-interval',
+ type: 'integer', value: 30,
+ description: 'number of seconds to wait between checks of the Kerberos credential cache')
+
+option('minimum-lifetime',
+ type: 'integer', value: 30,
+ description: 'minimum amount of time (m) a credential will have to be valid before we will ask the
user to get fresh credentials.')
+
+option('pkcs11',
+ type: 'string', value: '/usr/lib/opensc/opensc-pkcs11.so',
+ description: 'set path of PKCS11 smartcard helper')
+
+option('pam-plugin',
+ type: 'feature', value: 'auto',
+ description: 'wheter to build the pam plugin')
+
+option('pkinit',
+ type: 'feature', value: 'auto',
+ description: 'whether to enable pkinit support')
+
+option('etpo',
+ type: 'feature', value: 'disabled',
+ description: 'Build etpo parser')
diff --git a/plugins/meson.build b/plugins/meson.build
new file mode 100644
index 0000000..78d2feb
--- /dev/null
+++ b/plugins/meson.build
@@ -0,0 +1,50 @@
+
+dummy_plugin_sources = [
+ 'ka-plugin-dummy.c',
+ 'ka-plugin-dummy.h',
+]
+
+shared_module('ka-plugin-dummy',
+ dummy_plugin_sources,
+ include_directories: src_inc,
+ dependencies: gio_dep,
+ install: true,
+ install_dir: pluginsdir)
+
+if build_pam_plugin
+ pam_plugin_sources = [
+ 'ka-plugin-pam.c',
+ 'ka-plugin-pam.h',
+ ]
+
+ shared_module('ka-plugin-pam',
+ pam_plugin_sources,
+ include_directories: src_inc,
+ dependencies: [gio_dep, pam_dep],
+ install: true,
+ install_dir: pluginsdir)
+endif
+
+afs_plugin_sources = [
+ 'ka-plugin-afs.c',
+ 'ka-plugin-afs.h',
+]
+
+shared_module('ka-plugin-afs',
+ afs_plugin_sources,
+ include_directories: src_inc,
+ dependencies: gio_dep,
+ install: true,
+ install_dir: pluginsdir)
+
+gnomelock_plugin_sources = [
+ 'ka-plugin-gnomelock.c',
+ 'ka-plugin-gnomelock.h',
+]
+
+shared_module('ka-plugin-gnomelock',
+ gnomelock_plugin_sources,
+ include_directories: src_inc,
+ dependencies: gio_dep,
+ install: true,
+ install_dir: pluginsdir)
diff --git a/po/meson.build b/po/meson.build
new file mode 100644
index 0000000..f9e0d26
--- /dev/null
+++ b/po/meson.build
@@ -0,0 +1,2 @@
+i18n = import('i18n')
+i18n.gettext('krb5-auth-dialog', preset : 'glib')
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..c7be330
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,101 @@
+desktop_file = 'org.gnome.KrbAuthDialog.desktop'
+merged = i18n.merge_file('desktop',
+ input: desktop_file + '.in',
+ output: desktop_file,
+ po_dir: '../po',
+ install: true,
+ install_dir: desktopdir,
+ type: 'desktop'
+)
+
+merged = i18n.merge_file('autostart',
+ input: desktop_file + '.in',
+ output: 'krb5-auth-dialog.desktop',
+ po_dir: '../po',
+ install: true,
+ install_dir: join_paths(get_option('sysconfdir'), 'xdg', 'autostart'),
+ type: 'desktop'
+)
+
+serviceconf = configuration_data()
+serviceconf.set('BINDIR', bindir)
+service_file = 'org.gnome.KrbAuthDialog.service'
+configure_file(
+ input: service_file + '.in',
+ output: service_file,
+ configuration: serviceconf,
+ install: true,
+ install_dir: join_paths(get_option('datadir'), 'dbus-1', 'services'),
+)
+
+appstream_file = i18n.merge_file(
+ input: 'krb5-auth-dialog.appdata.xml.in',
+ output: 'krb5-auth-dialog.appdata.xml',
+ po_dir: '../po',
+ install: true,
+ install_dir: join_paths(get_option('datadir'), 'metainfo')
+)
+
+appstream_util = find_program('appstream-util', required: false)
+if appstream_util.found()
+ test('Validate appstream file', appstream_util,
+ args: ['validate', '--nonet', appstream_file]
+ )
+endif
+
+schema_src = 'org.gnome.KrbAuthDialog.gschema.xml'
+compiled = gnome.compile_schemas(build_by_default: true,
+ depend_files: files(schema_src))
+install_data(schema_src,
+ install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas')
+)
+
+compile_schemas = find_program('glib-compile-schemas', required: false)
+if compile_schemas.found()
+ test('Validate schema file', compile_schemas,
+ args: ['--strict', '--dry-run', meson.current_source_dir()]
+ )
+endif
+
+krb5_auth_dialog_resources = gnome.compile_resources(
+ 'krb5-auth-dialog-resource',
+ 'krb5-auth-dialog.gresource.xml',
+ source_dir: 'resources',
+ c_name: 'krb5_auth_dialog_resource',
+)
+
+krb5_auth_dialog_generated_sources = [
+ configure_file,
+ krb5_auth_dialog_resources,
+]
+
+krb5_auth_dialog_sources = [
+ 'dummy-strings.c',
+ 'ka-applet.c',
+ 'ka-closures.c',
+ 'ka-dbus.c',
+ 'ka-kerberos.c',
+ 'ka-main-window.c',
+ 'ka-plugin.c',
+ 'ka-plugin-loader.c',
+ 'ka-preferences.c',
+ 'ka-pwdialog.c',
+ 'ka-settings.c',
+ 'ka-tools.c',
+ krb5_auth_dialog_generated_sources,
+]
+
+krb5_auth_dialog_deps = [
+ gcr_dep,
+ gio_dep,
+ gmodule_dep,
+ gobject_dep,
+ gtk_dep,
+ krb5_dep,
+]
+
+krb5_auth_dialog = executable(
+ 'krb5-auth-dialog',
+ krb5_auth_dialog_sources,
+ dependencies: krb5_auth_dialog_deps,
+ install: true)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]