[yelp-tools] yelp-new.py: Converted yelp-new to python
- From: Shaun McCance <shaunm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [yelp-tools] yelp-new.py: Converted yelp-new to python
- Date: Sat, 16 Jan 2021 17:21:30 +0000 (UTC)
commit 7e610eb8d5d2fd8dfc28778ce1516a7b1bff6dc3
Author: Shaun McCance <shaunm redhat com>
Date: Sat Jan 16 12:20:41 2021 -0500
yelp-new.py: Converted yelp-new to python
templates/py/templates/concept.duck | 15 ++
templates/py/templates/concept.page | 18 ++
templates/py/templates/guide.duck | 17 ++
templates/py/templates/guide.page | 21 ++
templates/py/templates/info.ducktype.include | 33 +++
templates/py/templates/info.mallard.include | 35 +++
templates/py/templates/reference.duck | 58 +++++
templates/py/templates/reference.page | 65 ++++++
templates/py/templates/task.duck | 29 +++
templates/py/templates/task.page | 35 +++
tools/yelp-check.py | 10 +
tools/yelp-new.py | 317 +++++++++++++++++++++++++++
12 files changed, 653 insertions(+)
---
diff --git a/templates/py/templates/concept.duck b/templates/py/templates/concept.duck
new file mode 100644
index 00000000..0e6dff42
--- /dev/null
+++ b/templates/py/templates/concept.duck
@@ -0,0 +1,15 @@
+@ducktype/1.0
+[-] yelp-tmpl-desc Explanation of a concept or background information
+
+= {{TITLE}}
+ [topic .concept version=1.1]
+
+{{INCLUDE info.ducktype.include}}
+
+
+Provide as many paragraphs, lists, or media as necessary to explain.
+
+[list]
+. Next steps
+* Optionally, links to other things the user might do now.
+* But consider using seealso and other info links instead.
diff --git a/templates/py/templates/concept.page b/templates/py/templates/concept.page
new file mode 100644
index 00000000..16f56e4e
--- /dev/null
+++ b/templates/py/templates/concept.page
@@ -0,0 +1,18 @@
+<?yelp-tmpl-desc Explanation of a concept or background information?>
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic" style="concept" version="1.1"
+ id="{{ID}}">
+ <info>
+{{INCLUDE info.mallard.include}}
+ </info>
+
+ <title>{{TITLE}}</title>
+
+ <p>Provide as many paragraphs, lists, or media as necessary to explain.</p>
+
+ <list>
+ <title>Next steps</title>
+ <item><p>Optionally, links to other things the user might do now.</p></item>
+ <item><p>But consider using seealso and other info links instead.</p></item>
+ </list>
+</page>
diff --git a/templates/py/templates/guide.duck b/templates/py/templates/guide.duck
new file mode 100644
index 00000000..a7d13ea0
--- /dev/null
+++ b/templates/py/templates/guide.duck
@@ -0,0 +1,17 @@
+@ducktype/1.0
+[-] yelp-tmpl-desc Navigational glue for Mallard documents
+
+= {{TITLE}}
+ [guide version=1.1]
+
+{{INCLUDE info.ducktype.include}}
+
+
+Optionally, an introductory paragraph.
+
+[--
+ The links element is implicit, but you might want to add one
+ explicitly if you want to do link grouping or styles.
+[links topic .STYLE groups="GROUPS"]
+ . Optional title
+--]
diff --git a/templates/py/templates/guide.page b/templates/py/templates/guide.page
new file mode 100644
index 00000000..90b8a7dd
--- /dev/null
+++ b/templates/py/templates/guide.page
@@ -0,0 +1,21 @@
+<?yelp-tmpl-desc Navigational glue for Mallard documents?>
+<page xmlns="http://projectmallard.org/1.0/"
+ type="guide" version="1.1"
+ id="{{ID}}">
+ <info>
+{{INCLUDE info.mallard.include}}
+ </info>
+
+ <title>{{TITLE}}</title>
+
+ <p>Optionally, an introductory paragraph.</p>
+
+ <!--
+ The links element is implicit, but you might want to add one
+ explicitly if you want to do link grouping or styles.
+ <links type="topic" style="STYLE" groups="GROUPS">
+ <title>Optional title</title>
+ </links>
+ -->
+
+</page>
diff --git a/templates/py/templates/info.ducktype.include b/templates/py/templates/info.ducktype.include
new file mode 100644
index 00000000..fdc6de14
--- /dev/null
+++ b/templates/py/templates/info.ducktype.include
@@ -0,0 +1,33 @@
+[--
+ Recommended statuses: stub incomplete draft outdated review candidate final
+ Remove version attributes you don't use.
+--]
+@revision[version={{VERSION}} date={{DATE}} status=stub]
+
+@credit[author copyright]
+ @name {{NAME}}
+ @email {{EMAIL}}
+ @years {{YEAR}}
+
+[--
+ This puts a link to this topic on the index page.
+ Change the xref to link it from another guide.
+--]
+@link[guide >index]
+
+[--
+ Think about whether other pages should be in the seealso list.
+ The target page will automatically get a seealso link back.
+@link[seealso >someotherid]
+--]
+
+[--
+ Think about whether external resources should be in the seealso
+ list. These require a title.
+@link[seealso >>http://someurl]
+ @title Link title
+--]
+
+@desc Write a short page description here.
+
+@keywords comma-separated list, of keywords, for search
diff --git a/templates/py/templates/info.mallard.include b/templates/py/templates/info.mallard.include
new file mode 100644
index 00000000..d3209d78
--- /dev/null
+++ b/templates/py/templates/info.mallard.include
@@ -0,0 +1,35 @@
+ <!--
+ Recommended statuses: stub incomplete draft outdated review candidate final
+ Remove version attributes you don't use.
+ -->
+ <revision version="{{VERSION}}" date="{{DATE}}" status="stub"/>
+
+ <credit type="author copyright">
+ <name>{{NAME}}</name>
+ <email>{{EMAIL}}</email>
+ <years>{{YEAR}}</years>
+ </credit>
+
+ <!--
+ This puts a link to this topic on the index page.
+ Change the xref to link it from another guide.
+ -->
+ <link type="guide" xref="index"/>
+
+ <!--
+ Think about whether other pages should be in the seealso list.
+ The target page will automatically get a seealso link back.
+ <link type="seealso" xref="someotherid"/>
+ -->
+
+ <!--
+ Think about whether external resources should be in the seealso
+ list. These require a title.
+ <link type="seealso" href="http://someurl">
+ <title>Link title</title>
+ </link>
+ -->
+
+ <desc>Write a short page description here.</desc>
+
+ <keywords>comma-separated list, of keywords, for search</keywords>
diff --git a/templates/py/templates/reference.duck b/templates/py/templates/reference.duck
new file mode 100644
index 00000000..2d1a7705
--- /dev/null
+++ b/templates/py/templates/reference.duck
@@ -0,0 +1,58 @@
+@ducktype/1.0
+[-] yelp-tmpl-desc Lists or tables of information for quick lookup
+
+= {{TITLE}}
+ [topic .reference version=1.1]
+
+{{INCLUDE info.ducktype.include}}
+
+
+Optionally provide introductory text.
+Use terms lists, tables, and bullet lists for reference material.
+
+
+[terms]
+. Example terms list (title optional)
+
+- Term 1
+* Description
+
+- Term 2
+* Terms can have multiple paragraphs.
+
+ [screen]
+ Or any other block element.
+
+- Term 3.1
+- Term 3.2
+* Terms can also have multiple titles.
+
+
+[table]
+. Example table (title optional)
+
+[thead]
+[tr]
+- Column 1
+- Column 2
+
+[tbody]
+[tr]
+* Row 1, column 1
+* Row 1, column 2
+
+[tr]
+* Row 2, column 1
+* Row 2, column 2
+
+
+[list]
+. Example list (title optional)
+
+* Item 1
+
+* Item 2
+ * Subitem 2.1
+ * Subitem 2.2
+
+* Item 3
diff --git a/templates/py/templates/reference.page b/templates/py/templates/reference.page
new file mode 100644
index 00000000..3551452b
--- /dev/null
+++ b/templates/py/templates/reference.page
@@ -0,0 +1,65 @@
+<?yelp-tmpl-desc Lists or tables of information for quick lookup?>
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic" style="reference" version="1.1"
+ id="{{ID}}">
+ <info>
+{{INCLUDE info.mallard.include}}
+ </info>
+
+ <title>{{TITLE}}</title>
+
+ <p>Optionally provide introductory text.
+ Use terms lists, tables, and bullet lists for reference material.</p>
+
+ <terms>
+ <title>Example terms list (title optional)</title>
+ <item>
+ <title>Term 1</title>
+ <para>Description</para>
+ </item>
+ <item>
+ <title>Term 2</title>
+ <p>Terms can have multiple paragraphs.</p>
+ <screen>Or any other block element.</screen>
+ </item>
+ <item>
+ <title>Term 3.1</title>
+ <title>Term 3.2</title>
+ <p>Terms can also have multiple titles.</p>
+ </item>
+ </terms>
+
+ <table>
+ <title>Example table (title optional)</title>
+ <thead>
+ <tr>
+ <th>Column 1</th>
+ <th>Column 2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Row 1, column 1</td>
+ <td>Row 1, column 2</td>
+ </tr>
+ <tr>
+ <td>Row 2, column 1</td>
+ <td>Row 2, column 2</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <list>
+ <title>Example list (title optional)</title>
+ <item><p>Item 1</p></item>
+ <item>
+ <p>Item 2</p>
+ <list>
+ <item><p>Subitem 2.1</p></item>
+ <item><p>Subitem 2.2</p></item>
+ </list>
+ </item>
+ <item><p>Item 3</p></item>
+ </list>
+
+</page>
diff --git a/templates/py/templates/task.duck b/templates/py/templates/task.duck
new file mode 100644
index 00000000..aab510c5
--- /dev/null
+++ b/templates/py/templates/task.duck
@@ -0,0 +1,29 @@
+@ducktype/1.0
+[-] yelp-tmpl-desc Description of how to accomplish a user task
+
+= {{TITLE}}
+ [topic .task version=1.1]
+
+{{INCLUDE info.ducktype.include}}
+
+
+Short introductory text: Write a couple sentences about what the
+user is doing here, and why they might want to do it.
+
+[list]
+. Prerequisites
+* Optionally, list things the user needs to know or do first.
+* Use links to other pages whenever they make sense.
+
+[steps]
+. Optional title if different from page title or if prereqs present
+* First step...
+* Second step...
+* Third step...
+
+Optionally, write expected results if non-obvious.
+
+[list]
+. Next steps
+* Optionally, links to other things the user might do now.
+* But consider using seealso and other info links instead.
diff --git a/templates/py/templates/task.page b/templates/py/templates/task.page
new file mode 100644
index 00000000..ba6da8b8
--- /dev/null
+++ b/templates/py/templates/task.page
@@ -0,0 +1,35 @@
+<?yelp-tmpl-desc Description of how to accomplish a user task?>
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic" style="task" version="1.1"
+ id="{{ID}}">
+ <info>
+{{INCLUDE info.mallard.include}}
+ </info>
+
+ <title>{{TITLE}}</title>
+
+ <p>Short introductory text: Write a couple sentences about what the
+ user is doing here, and why they might want to do it.</p>
+
+ <list>
+ <title>Prerequisites</title>
+ <item><p>Optionally, list things the user needs to know or do first.</p></item>
+ <item><p>Use links to other pages whenever they make sense.</p></item>
+ </list>
+
+ <steps>
+ <title>Optional title if different from page title or if prereqs present</title>
+ <item><p>First step...</p></item>
+ <item><p>Second step...</p></item>
+ <item><p>Third step...</p></item>
+ </steps>
+
+ <p>Optionally, write expected results if non-obvious.</p>
+
+ <list>
+ <title>Next steps</title>
+ <item><p>Optionally, links to other things the user might do now.</p></item>
+ <item><p>But consider using seealso and other info links instead.</p></item>
+ </list>
+
+</page>
diff --git a/tools/yelp-check.py b/tools/yelp-check.py
index 652062eb..a3eb4866 100644
--- a/tools/yelp-check.py
+++ b/tools/yelp-check.py
@@ -152,6 +152,9 @@ class Checker:
val = self.config.get('check', arg, fallback=None)
if val is not None:
return (val == 'true')
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return (val == 'true')
return False
def get_option_str(self, arg):
@@ -165,6 +168,9 @@ class Checker:
val = self.config.get('check', arg, fallback=None)
if val is not None:
return val
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return val
return None
def get_option_list(self, arg):
@@ -181,6 +187,9 @@ class Checker:
val = self.config.get('check', arg, fallback=None)
if val is not None:
return val.replace(',', ' ').split()
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return val.replace(',', ' ').split()
return None
def iter_files(self, sitedir=None):
@@ -208,6 +217,7 @@ class Checker:
for fname in os.listdir(filepath):
newpath = os.path.join(filepath, fname)
if os.path.isdir(newpath):
+ # FIXME https://github.com/projectmallard/pintail/issues/36
if fname == '__pintail__':
continue
for infile in self.iter_site(newpath, sitedir + fname + '/'):
diff --git a/tools/yelp-new.py b/tools/yelp-new.py
new file mode 100644
index 00000000..5b8f6f0e
--- /dev/null
+++ b/tools/yelp-new.py
@@ -0,0 +1,317 @@
+#!/bin/python3
+#
+# yelp-new
+# Copyright (C) 2010-2020 Shaun McCance <shaunm gnome org>
+#
+# This program 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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import configparser
+import datetime
+import os
+import subprocess
+import sys
+
+# FIXME: don't hardcode this
+DATADIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'templates', 'py')
+
+class YelpNew:
+ arguments = [
+ ('help', '-h', None, 'Show this help and exit'),
+ ('stub', None, None, 'Create a stub file with .stub appended'),
+ ('tmpl', None, None, 'Copy an installed template to a local template'),
+ ('version', '-v', 'VERS', 'Specify the version number to substitute')
+ ]
+
+ def __init__(self):
+ self.options = {}
+ self.fileargs = []
+ self.parse_args(sys.argv[1:])
+ self.config = configparser.ConfigParser()
+ try:
+ self.config.read('.yelp-tools.cfg')
+ except:
+ self.config = None
+
+
+ def parse_args(self, args):
+ while len(args) > 0:
+ argdef = None
+ if args[0].startswith('--'):
+ for arg_ in self.arguments:
+ if args[0] == '--' + arg_[0]:
+ argdef = arg_
+ break
+ if argdef is None:
+ self.print_usage()
+ return 1
+ elif args[0].startswith('-'):
+ for arg_ in self.arguments:
+ if args[0] == arg_[1]:
+ argdef = arg_
+ break
+ if argdef is None:
+ self.print_usage()
+ return 1
+ if argdef is not None:
+ takesarg = (argdef[2] is not None)
+ if takesarg:
+ if len(args) < 2:
+ self.print_usage()
+ return 1
+ self.options.setdefault(argdef[0], [])
+ self.options[argdef[0]].append(args[1])
+ args = args[2:]
+ else:
+ self.options[argdef[0]] = True
+ args = args[1:]
+ else:
+ self.fileargs.append(args[0])
+ args = args[1:]
+
+
+ def get_option_bool(self, arg):
+ if arg in self.options:
+ return self.options[arg] == True
+ if self.config is not None:
+ val = self.config.get('new', arg, fallback=None)
+ if val is not None:
+ return (val == 'true')
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return (val == 'true')
+ return False
+
+
+ def get_option_str(self, arg):
+ if arg in self.options:
+ if isinstance(self.options[arg], list):
+ return self.options[arg][-1]
+ if self.config is not None:
+ val = self.config.get('new', arg, fallback=None)
+ if val is not None:
+ return val
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return val
+ return None
+
+
+ def get_replacements(self, pageid):
+ repl = {'ID' : pageid}
+ if len(self.fileargs) > 2:
+ repl['TITLE'] = ' '.join(self.fileargs[2:])
+ else:
+ repl['TITLE'] = 'TITLE'
+ today = datetime.datetime.now()
+ repl['DATE'] = today.strftime('%Y-%m-%d')
+ repl['YEAR'] = today.strftime('%Y')
+
+ username = None
+ useremail = None
+ isgit = False
+ isbzr = False
+ cwd = os.getcwd()
+ while cwd:
+ if os.path.exists(os.path.join(cwd, '.git')):
+ isgit = True
+ break
+ if os.path.exists(os.path.join(cwd, '.bzr')):
+ isbzr = True
+ break
+ newcwd = os.path.dirname(cwd)
+ if newcwd == cwd:
+ break
+ cwd = newcwd
+ if isbzr:
+ try:
+ who = subprocess.run(['bzr', 'whoami'], check=True,
+ capture_output=True, encoding='utf8')
+ username, useremail = who.stdout.split('<')
+ username = username.strip()
+ useremail = useremail.split('>')[0].strip()
+ except:
+ username = None
+ useremail = None
+ if username is None:
+ try:
+ who = subprocess.run(['git', 'config', 'user.name'], check=True,
+ capture_output=True, encoding='utf8')
+ username = who.stdout.strip()
+ who = subprocess.run(['git', 'config', 'user.email'], check=True,
+ capture_output=True, encoding='utf8')
+ useremail = who.stdout.strip()
+ except:
+ username = None
+ useremail = None
+ repl['NAME'] = username or 'YOUR NAME'
+ repl['EMAIL'] = useremail or 'YOUR EMAIL ADDRESS'
+ repl['VERSION'] = self.get_option_str('version') or 'VERSION.NUMBER'
+ return repl
+
+
+ def main(self):
+ if len(self.fileargs) < 2:
+ self.print_usage()
+ return 1
+
+ tmpl = self.fileargs[0]
+ if '.' not in tmpl:
+ tmpl = tmpl + '.page'
+ ext = '.page'
+ elif tmpl.endswith('.page'):
+ ext = '.page'
+ elif tmpl.endswith('.duck'):
+ ext = '.duck'
+ if self.get_option_bool('stub'):
+ ext = ext + '.stub'
+ tmplfile = os.path.join(os.getcwd(), tmpl + '.tmpl')
+ if not os.path.exists(tmplfile):
+ tmplfile = os.path.join(DATADIR, 'templates', tmpl)
+ if not os.path.exists(tmplfile):
+ print('No template found named ' + tmpl, file=sys.stderr)
+ sys.exit(1)
+ pageid = self.fileargs[1]
+ istmpl = self.get_option_bool('tmpl')
+ if istmpl:
+ ext = ext + '.tmpl'
+ repl = {}
+ else:
+ repl = self.get_replacements(pageid)
+ def _writeout(outfile, infilename, depth=0):
+ if depth > 10:
+ # We could do this smarter by keeping a stack of infilenames, but why?
+ print('Recursion limit reached for template includes', file=sys.stderr)
+ sys.exit(1)
+ for line in open(infilename):
+ if (not istmpl) and line.startswith('<?yelp-tmpl-desc'):
+ continue
+ if (not istmpl) and line.startswith('[-] yelp-tmpl-desc'):
+ continue
+ while line is not None and '{{' in line:
+ before, after = line.split('{{', maxsplit=1)
+ if '}}' in after:
+ var, after = after.split('}}', maxsplit=1)
+ outfile.write(before)
+ isinclude = var.startswith('INCLUDE ')
+ if isinclude:
+ newfile = os.path.join(os.path.dirname(infilename), var[8:].strip())
+ _writeout(outfile, newfile, depth=depth+1)
+ elif istmpl:
+ outfile.write('{{' + var + '}}')
+ else:
+ outfile.write(repl.get(var, '{{' + var + '}}'))
+ if isinclude and after == '\n':
+ line = None
+ else:
+ line = after
+ else:
+ outfile.write(line)
+ line = None
+ if line is not None:
+ outfile.write(line)
+
+ if os.path.exists(pageid + ext):
+ print('Output file ' + pageid + ext + ' already exists', file=sys.stderr)
+ sys.exit(1)
+ with open(pageid + ext, 'w') as outfile:
+ _writeout(outfile, tmplfile)
+
+
+ def print_usage(self):
+ print('Usage: yelp-new [OPTIONS] <TEMPLATE> <ID> [TITLE]\n')
+ print('Create a new file from an installed or local template file,\n' +
+ 'or create a new local template. TEMPLATE must be the name of\n' +
+ 'an installed or local template. ID is a page ID (and base\n' +
+ 'filename) for the new page. The optional TITLE argument\n'
+ 'provides the page title\n')
+ print('Options:')
+ maxarglen = 2
+ args = []
+ for arg in self.arguments:
+ argkey = '--' + arg[0]
+ if arg[1] is not None:
+ argkey = arg[1] + ', ' + argkey
+ if arg[2] is not None:
+ argkey = argkey + ' ' + arg[2]
+ args.append((argkey, arg[3]))
+ for arg in args:
+ maxarglen = max(maxarglen, len(arg[0]) + 1)
+ for arg in args:
+ print(' ' + (arg[0]).ljust(maxarglen) + ' ' + arg[1])
+ localpages = []
+ localducks = []
+ installedpages = []
+ installedducks = []
+ descs = {}
+ maxlen = 0
+ def _getdesc(fpath):
+ for line in open(fpath):
+ if line.startswith('<?yelp-tmpl-desc '):
+ s = line[16:].strip()
+ if s.endswith('?>'):
+ s = s[:-2]
+ return s
+ if line.startswith('[-] yelp-tmpl-desc'):
+ return line[18:].strip()
+ return ''
+ for fname in os.listdir(os.getcwd()):
+ if fname.endswith('.page.tmpl'):
+ fname = fname[:-5]
+ maxlen = max(maxlen, len(fname))
+ localpages.append(fname)
+ elif fname.endswith('.duck.tmpl'):
+ fname = fname[:-5]
+ maxlen = max(maxlen, len(fname))
+ localducks.append(fname)
+ else:
+ continue
+ descs[fname] = _getdesc(os.path.join(os.getcwd(), fname + '.tmpl'))
+ for fname in os.listdir(os.path.join(DATADIR, 'templates')):
+ if fname.endswith('.page'):
+ if fname in localpages:
+ continue
+ maxlen = max(maxlen, len(fname))
+ installedpages.append(fname)
+ elif fname.endswith('.duck'):
+ if fname in localducks:
+ continue
+ maxlen = max(maxlen, len(fname))
+ installedducks.append(fname)
+ else:
+ continue
+ descs[fname] = _getdesc(os.path.join(DATADIR, 'templates', fname))
+ if len(localpages) > 0:
+ print('\nLocal Mallard Templates:')
+ for page in localpages:
+ print(' ' + page.ljust(maxlen) + ' ' + descs.get(page, ''))
+ if len(localducks) > 0:
+ print('\nLocal Ducktype Templates:')
+ for duck in localducks:
+ print(' ' + duck.ljust(maxlen) + ' ' + descs.get(duck, ''))
+ if len(installedpages) > 0:
+ print('\nInstalled Mallard Templates:')
+ for page in installedpages:
+ print(' ' + page.ljust(maxlen) + ' ' + descs.get(page, ''))
+ if len(installedducks) > 0:
+ print('\nInstalled Ducktype Templates:')
+ for duck in installedducks:
+ print(' ' + duck.ljust(maxlen) + ' ' + descs.get(duck, ''))
+
+
+if __name__ == '__main__':
+ try:
+ sys.exit(YelpNew().main())
+ except KeyboardInterrupt:
+ sys.exit(1)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]