[yelp-tools] yelp-new.py: Converted yelp-new to python



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]