GNU bug report logs - #48934
[PATCH 0/2] Some improvements to (gnu services configuration)

Previous Next

Package: guix-patches;

Reported by: Xinglu Chen <public <at> yoctocell.xyz>

Date: Wed, 9 Jun 2021 13:05:01 UTC

Severity: normal

Tags: patch

Done: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 48934 in the body.
You can then email your comments to 48934 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#48934; Package guix-patches. (Wed, 09 Jun 2021 13:05:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Xinglu Chen <public <at> yoctocell.xyz>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Wed, 09 Jun 2021 13:05:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Xinglu Chen <public <at> yoctocell.xyz>
To: guix-patches <at> gnu.org
Cc: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
Subject: [PATCH 0/2] Some improvements to (gnu services configuration)
Date: Wed, 09 Jun 2021 15:04:31 +0200
[Message part 1 (text/plain, inline)]
This series contains some improvements to the (gnu services
configuration) module.

The first patch changes the formatting of the generated documentation
for configuration records.  Previously, the generated documentation
looked a bit different from the ones that were to generated, compare the
docs for ‘getmail-configuration’ (generated) and ‘openssh-configuration’
(not generated).

--8<---------------cut here---------------start------------->8---
   Available ‘getmail-configuration’ fields are:

 -- ‘getmail-configuration’ parameter: symbol name
     A symbol to identify the getmail service.

     Defaults to ‘"unset"’.

 -- ‘getmail-configuration’ parameter: package package
     The getmail package to use.
--8<---------------cut here---------------end--------------->8---

--8<---------------cut here---------------start------------->8---
 -- Data Type: openssh-configuration
     This is the configuration record for OpenSSH’s ‘sshd’.

     ‘openssh’ (default OPENSSH)
          The Openssh package to use.

     ‘pid-file’ (default: ‘"/var/run/sshd.pid"’)
          Name of the file where ‘sshd’ writes its PID.

     ‘port-number’ (default: ‘22’)
          TCP port on which ‘sshd’ listens for incoming connections.
--8<---------------cut here---------------end--------------->8---

The first patch will make the generated documentation look at lot more
similiar to the hand-written ones.

--8<---------------cut here---------------start------------->8---
 -- Data Type: getmail-configuration
     Available ‘getmail-configuration’ fields are:

     ‘name’ (default: ‘"unset"’) (type: symbol)
          A symbol to identify the getmail service.

     ‘package’ (default: ‘getmail’) (type: package)
          The getmail package to use.
--8<---------------cut here---------------end--------------->8---

If you paid close attention you will also notice that the old generated
docs didn’t specify the default value of the ‘package’ field, whereas
the new docs do.  This brings us to the second patch, it looks the
package and shows the value of the ‘name’ field of the package.  This
will only show the correct package name if the ‘name’ field and the
Scheme variable corresponding to the package are the same, in most cases
it is, so I don’t think it would be a huge deal.

Xinglu Chen (2):
  services: configuration: Change formatting of generated documentation.
  services: configuration: Show default value when it is a package.

 gnu/services/configuration.scm | 62 ++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 26 deletions(-)


base-commit: 86bb77608d375043f837583332a7c852ea2080ec
-- 
2.32.0


[signature.asc (application/pgp-signature, inline)]

Information forwarded to guix-patches <at> gnu.org:
bug#48934; Package guix-patches. (Wed, 09 Jun 2021 13:07:02 GMT) Full text and rfc822 format available.

Message #8 received at 48934 <at> debbugs.gnu.org (full text, mbox):

From: Xinglu Chen <public <at> yoctocell.xyz>
To: 48934 <at> debbugs.gnu.org
Subject: [PATCH 1/2] services: configuration: Change formatting of generated
Date: Wed, 09 Jun 2021 15:06:26 +0200
* gnu/services/configuration (generate-documentation): Make the formatting of
  the generated docs more consistent with the rest of the docs in the “Services”
  section of the manual.
---
 gnu/services/configuration.scm | 54 ++++++++++++++++++----------------
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/gnu/services/configuration.scm b/gnu/services/configuration.scm
index f23840ee6d..abcbc70520 100644
--- a/gnu/services/configuration.scm
+++ b/gnu/services/configuration.scm
@@ -239,32 +239,34 @@ does not have a default value" field kind)))
   (define (generate configuration-name)
     (match (assq-ref documentation configuration-name)
       ((fields . sub-documentation)
-       `((para "Available " (code ,(str configuration-name)) " fields are:")
-         ,@(map
-            (lambda (f)
-              (let ((field-name (configuration-field-name f))
-                    (field-type (configuration-field-type f))
-                    (field-docs (cdr (texi-fragment->stexi
-                                      (configuration-field-documentation f))))
-                    (default (catch #t
-                               (configuration-field-default-value-thunk f)
-                               (lambda _ '%invalid))))
-                (define (show-default? val)
-                  (or (string? val) (number? val) (boolean? val)
-                      (and (symbol? val) (not (eq? val '%invalid)))
-                      (and (list? val) (and-map show-default? val))))
-                `(deftypevr (% (category
-                                (code ,(str configuration-name)) " parameter")
-                               (data-type ,(str field-type))
-                               (name ,(str field-name)))
-                   ,@field-docs
-                   ,@(if (show-default? default)
-                         `((para "Defaults to " (samp ,(str default)) "."))
-                         '())
-                   ,@(append-map
-                      generate
-                      (or (assq-ref sub-documentation field-name) '())))))
-            fields)))))
+       `((deftp (% (category "Data Type") (name ,(str configuration-name)))
+           (para "Available " (code ,(str configuration-name)) " fields are:")
+           (table (% (formatter (asis)))
+                  ,@(map
+                     (lambda (f)
+                       (let ((field-name (configuration-field-name f))
+                             (field-type (configuration-field-type f))
+                             (field-docs (cdr (texi-fragment->stexi
+                                               (configuration-field-documentation f))))
+                             (default (catch #t
+                                        (configuration-field-default-value-thunk f)
+                                        (lambda _ '%invalid))))
+                         (define (show-default? val)
+                           (or (string? val) (number? val) (boolean? val)
+                               (and (symbol? val) (not (eq? val '%invalid)))
+                               (and (list? val) (and-map show-default? val))))
+                         `(entry (% (heading (code ,(str field-name))
+                                             ,@(if (show-default? default)
+                                                   `(" (default: " (code ,(str default)) ")")
+                                                   '())
+                                             " (type: "
+                                             ,(str field-type)
+                                             ")"))
+                                 (para ,@field-docs)
+                                 ,@(append-map
+                                    generate
+                                    (or (assq-ref sub-documentation field-name) '())))))
+                     fields)))))))
   (stexi->texi `(*fragment* . ,(generate documentation-name))))
 
 (define (configuration->documentation configuration-symbol)
-- 
2.32.0






Information forwarded to guix-patches <at> gnu.org:
bug#48934; Package guix-patches. (Wed, 09 Jun 2021 13:07:02 GMT) Full text and rfc822 format available.

Message #11 received at 48934 <at> debbugs.gnu.org (full text, mbox):

From: Xinglu Chen <public <at> yoctocell.xyz>
To: 48934 <at> debbugs.gnu.org
Subject: [PATCH 2/2] services: configuration: Show default value when it is a
Date: Wed, 09 Jun 2021 15:06:27 +0200
* gnu/services/configuration.scm (generate-documentation): If the default
  value of a field is a package, show the value of the ‘name’ field of the
  package.  This might not be the correct name in some cases though.
---
 gnu/services/configuration.scm | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/gnu/services/configuration.scm b/gnu/services/configuration.scm
index abcbc70520..99687d065a 100644
--- a/gnu/services/configuration.scm
+++ b/gnu/services/configuration.scm
@@ -252,12 +252,20 @@ does not have a default value" field kind)))
                                         (configuration-field-default-value-thunk f)
                                         (lambda _ '%invalid))))
                          (define (show-default? val)
-                           (or (string? val) (number? val) (boolean? val)
+                           (or (string? val) (number? val) (boolean? val) (package? val)
                                (and (symbol? val) (not (eq? val '%invalid)))
                                (and (list? val) (and-map show-default? val))))
+
+                         (define (show-default val)
+                           (cond
+                            ((package? val)
+                             ;; Maybe not always correct.
+                             (package-name val))
+                            (else (str val))))
+
                          `(entry (% (heading (code ,(str field-name))
                                              ,@(if (show-default? default)
-                                                   `(" (default: " (code ,(str default)) ")")
+                                                   `(" (default: " (code ,(show-default default)) ")")
                                                    '())
                                              " (type: "
                                              ,(str field-type)
-- 
2.32.0






Information forwarded to guix-patches <at> gnu.org:
bug#48934; Package guix-patches. (Mon, 02 Aug 2021 18:11:01 GMT) Full text and rfc822 format available.

Message #14 received at 48934 <at> debbugs.gnu.org (full text, mbox):

From: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
To: Xinglu Chen <public <at> yoctocell.xyz>
Cc: 48934 <at> debbugs.gnu.org
Subject: Re: bug#48934: [PATCH 0/2] Some improvements to (gnu services
 configuration)
Date: Mon, 02 Aug 2021 14:10:01 -0400
Hello Xinglu!

Xinglu Chen <public <at> yoctocell.xyz> writes:

> * gnu/services/configuration (generate-documentation): Make the formatting of
>   the generated docs more consistent with the rest of the docs in the “Services”
>   section of the manual.

I've modified the commit message to be more in line with the GNU change
log style (see 'info (standards) Style of Change Logs'), like so:

  services: configuration: Uniformize the generated documentation.

  Make the formatting of the generated docs more consistent with the rest of the
  docs in the “Services” section of the manual.

  * gnu/services/configuration (generate-documentation): Represent the data type
  documentation of a field using a DEFTP table rather than DEFTYPEVR elements.

> ---
>  gnu/services/configuration.scm | 54 ++++++++++++++++++----------------
>  1 file changed, 28 insertions(+), 26 deletions(-)
>
> diff --git a/gnu/services/configuration.scm b/gnu/services/configuration.scm
> index f23840ee6d..abcbc70520 100644
> --- a/gnu/services/configuration.scm
> +++ b/gnu/services/configuration.scm
> @@ -239,32 +239,34 @@ does not have a default value" field kind)))
>    (define (generate configuration-name)
>      (match (assq-ref documentation configuration-name)
>        ((fields . sub-documentation)
> -       `((para "Available " (code ,(str configuration-name)) " fields are:")
> -         ,@(map
> -            (lambda (f)
> -              (let ((field-name (configuration-field-name f))
> -                    (field-type (configuration-field-type f))
> -                    (field-docs (cdr (texi-fragment->stexi
> -                                      (configuration-field-documentation f))))
> -                    (default (catch #t
> -                               (configuration-field-default-value-thunk f)
> -                               (lambda _ '%invalid))))
> -                (define (show-default? val)
> -                  (or (string? val) (number? val) (boolean? val)
> -                      (and (symbol? val) (not (eq? val '%invalid)))
> -                      (and (list? val) (and-map show-default? val))))
> -                `(deftypevr (% (category
> -                                (code ,(str configuration-name)) " parameter")
> -                               (data-type ,(str field-type))
> -                               (name ,(str field-name)))
> -                   ,@field-docs
> -                   ,@(if (show-default? default)
> -                         `((para "Defaults to " (samp ,(str default)) "."))
> -                         '())
> -                   ,@(append-map
> -                      generate
> -                      (or (assq-ref sub-documentation field-name) '())))))
> -            fields)))))
> +       `((deftp (% (category "Data Type") (name ,(str configuration-name)))
> +           (para "Available " (code ,(str configuration-name)) " fields are:")
> +           (table (% (formatter (asis)))
> +                  ,@(map
> +                     (lambda (f)
> +                       (let ((field-name (configuration-field-name f))
> +                             (field-type (configuration-field-type f))
> +                             (field-docs (cdr (texi-fragment->stexi
> +                                               (configuration-field-documentation f))))
> +                             (default (catch #t
> +                                        (configuration-field-default-value-thunk f)
> +                                        (lambda _ '%invalid))))
> +                         (define (show-default? val)
> +                           (or (string? val) (number? val) (boolean? val)
> +                               (and (symbol? val) (not (eq? val '%invalid)))
> +                               (and (list? val) (and-map show-default? val))))
> +                         `(entry (% (heading (code ,(str field-name))
> +                                             ,@(if (show-default? default)
> +                                                   `(" (default: " (code ,(str default)) ")")
> +                                                   '())
> +                                             " (type: "
> +                                             ,(str field-type)
> +                                             ")"))
> +                                 (para ,@field-docs)
> +                                 ,@(append-map
> +                                    generate
> +                                    (or (assq-ref sub-documentation field-name) '())))))
> +                     fields)))))))
>    (stexi->texi `(*fragment* . ,(generate documentation-name))))
>  
>  (define (configuration->documentation configuration-symbol)

I've used this opportunity to re-indent the code a bit, so that it'd fit
under 80 characters column width.

Thanks for this neat improvement!

Maxim




Reply sent to Maxim Cournoyer <maxim.cournoyer <at> gmail.com>:
You have taken responsibility. (Mon, 02 Aug 2021 18:22:01 GMT) Full text and rfc822 format available.

Notification sent to Xinglu Chen <public <at> yoctocell.xyz>:
bug acknowledged by developer. (Mon, 02 Aug 2021 18:22:02 GMT) Full text and rfc822 format available.

Message #19 received at 48934-done <at> debbugs.gnu.org (full text, mbox):

From: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
To: Xinglu Chen <public <at> yoctocell.xyz>
Cc: 48934-done <at> debbugs.gnu.org
Subject: Re: bug#48934: [PATCH 0/2] Some improvements to (gnu services
 configuration)
Date: Mon, 02 Aug 2021 14:21:07 -0400
Hello again,

Xinglu Chen <public <at> yoctocell.xyz> writes:

> * gnu/services/configuration.scm (generate-documentation): If the default
>   value of a field is a package, show the value of the ‘name’ field of the
>   package.  This might not be the correct name in some cases though.

Here also, I've edited the commit message like so:

  services: configuration: Derive the default value from the package variable.

  If the type of a configuration field is a package, show the name of its
  package *variable* as the default value.

  * gnu/services/configuration.scm (generate-documentation){show-default}
  {package->symbol}: New nested procedures.  Use them to format the field
  entries.

> ---
>  gnu/services/configuration.scm | 12 ++++++++++--
>  1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/gnu/services/configuration.scm b/gnu/services/configuration.scm
> index abcbc70520..99687d065a 100644
> --- a/gnu/services/configuration.scm
> +++ b/gnu/services/configuration.scm
> @@ -252,12 +252,20 @@ does not have a default value" field kind)))
>                                          (configuration-field-default-value-thunk f)
>                                          (lambda _ '%invalid))))
>                           (define (show-default? val)
> -                           (or (string? val) (number? val) (boolean? val)
> +                           (or (string? val) (number? val) (boolean? val) (package? val)
>                                 (and (symbol? val) (not (eq? val '%invalid)))
>                                 (and (list? val) (and-map show-default? val))))
> +
> +                         (define (show-default val)
> +                           (cond
> +                            ((package? val)
> +                             ;; Maybe not always correct.
> +                             (package-name val))
> +                            (else (str val))))
> +
>                           `(entry (% (heading (code ,(str field-name))
>                                               ,@(if (show-default? default)
> -                                                   `(" (default: " (code ,(str default)) ")")
> +                                                   `(" (default: " (code ,(show-default default)) ")")
>                                                     '())
>                                               " (type: "
>                                               ,(str field-type)

I've found a (rather hacky?) way to derive a package's symbol name
instead of using its name, which eliminates the risk of a mismatch,
using such procedure:

--8<---------------cut here---------------start------------->8---
@@ -252,6 +254,21 @@ does not have a default value" field kind)))
 ;; A little helper to make it easier to document all those fields.
 (define (generate-documentation documentation documentation-name)
   (define (str x) (object->string x))
+
+  (define (package->symbol package)
+    "Return the first symbol name of a package that matches PACKAGE, else #f."
+    (let* ((module (file-name->module-name
+                    (location-file (package-location package))))
+           (symbols (filter-map
+                     identity
+                     (module-map (lambda (symbol var)
+                                   (and (equal? package (variable-ref var))
+                                        symbol))
+                                 (resolve-module module)))))
+      (if (null? symbols)
+          #f
+          (first symbols))))
+
   (define (generate configuration-name)
     (match (assq-ref documentation configuration-name)
       ((fields . sub-documentation)
@@ -270,14 +287,21 @@ does not have a default value" field kind)))
                                   (lambda _ '%invalid))))
                    (define (show-default? val)
                      (or (string? val) (number? val) (boolean? val)
+                         (package? val)
                          (and (symbol? val) (not (eq? val '%invalid)))
                          (and (list? val) (and-map show-default? val))))
 
+                   (define (show-default val)
+                     (cond
+                      ((package? val)
+                       (symbol->string (package->symbol val)))
+                      (else (str val))))
+
                    `(entry (% (heading
                                (code ,(str field-name))
                                ,@(if (show-default? default)
                                      `(" (default: "
--8<---------------cut here---------------end--------------->8---

Tested it to generate the new Jami service documentation, and pushed as
commit 8e1f94421873777c6bb0b83daa4f81cbacc8b3ff.

I think (guix services configuration) is starting to look good!  Thanks
to your efforts toward improving the module.

Closing.

Maxim




Information forwarded to guix-patches <at> gnu.org:
bug#48934; Package guix-patches. (Tue, 03 Aug 2021 07:26:02 GMT) Full text and rfc822 format available.

Message #22 received at 48934-done <at> debbugs.gnu.org (full text, mbox):

From: Xinglu Chen <public <at> yoctocell.xyz>
To: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
Cc: 48934-done <at> debbugs.gnu.org
Subject: Re: bug#48934: [PATCH 0/2] Some improvements to (gnu services
 configuration)
Date: Tue, 03 Aug 2021 09:24:46 +0200
[Message part 1 (text/plain, inline)]
On Mon, Aug 02 2021, Maxim Cournoyer wrote:

> Hello again,
>
> Xinglu Chen <public <at> yoctocell.xyz> writes:
>
>> * gnu/services/configuration.scm (generate-documentation): If the default
>>   value of a field is a package, show the value of the ‘name’ field of the
>>   package.  This might not be the correct name in some cases though.
>
> Here also, I've edited the commit message like so:
>
>   services: configuration: Derive the default value from the package variable.
>
>   If the type of a configuration field is a package, show the name of its
>   package *variable* as the default value.
>
>   * gnu/services/configuration.scm (generate-documentation){show-default}
>   {package->symbol}: New nested procedures.  Use them to format the field
>   entries.

Thanks, I should probably re-read the manual.  :)

>> ---
>>  gnu/services/configuration.scm | 12 ++++++++++--
>>  1 file changed, 10 insertions(+), 2 deletions(-)
>>
>> diff --git a/gnu/services/configuration.scm b/gnu/services/configuration.scm
>> index abcbc70520..99687d065a 100644
>> --- a/gnu/services/configuration.scm
>> +++ b/gnu/services/configuration.scm
>> @@ -252,12 +252,20 @@ does not have a default value" field kind)))
>>                                          (configuration-field-default-value-thunk f)
>>                                          (lambda _ '%invalid))))
>>                           (define (show-default? val)
>> -                           (or (string? val) (number? val) (boolean? val)
>> +                           (or (string? val) (number? val) (boolean? val) (package? val)
>>                                 (and (symbol? val) (not (eq? val '%invalid)))
>>                                 (and (list? val) (and-map show-default? val))))
>> +
>> +                         (define (show-default val)
>> +                           (cond
>> +                            ((package? val)
>> +                             ;; Maybe not always correct.
>> +                             (package-name val))
>> +                            (else (str val))))
>> +
>>                           `(entry (% (heading (code ,(str field-name))
>>                                               ,@(if (show-default? default)
>> -                                                   `(" (default: " (code ,(str default)) ")")
>> +                                                   `(" (default: " (code ,(show-default default)) ")")
>>                                                     '())
>>                                               " (type: "
>>                                               ,(str field-type)
>
> I've found a (rather hacky?) way to derive a package's symbol name
> instead of using its name, which eliminates the risk of a mismatch,
> using such procedure:
>
> --8<---------------cut here---------------start------------->8---
> @@ -252,6 +254,21 @@ does not have a default value" field kind)))
>  ;; A little helper to make it easier to document all those fields.
>  (define (generate-documentation documentation documentation-name)
>    (define (str x) (object->string x))
> +
> +  (define (package->symbol package)
> +    "Return the first symbol name of a package that matches PACKAGE, else #f."
> +    (let* ((module (file-name->module-name
> +                    (location-file (package-location package))))
> +           (symbols (filter-map
> +                     identity
> +                     (module-map (lambda (symbol var)
> +                                   (and (equal? package (variable-ref var))
> +                                        symbol))
> +                                 (resolve-module module)))))
> +      (if (null? symbols)
> +          #f
> +          (first symbols))))
> +
>    (define (generate configuration-name)
>      (match (assq-ref documentation configuration-name)
>        ((fields . sub-documentation)
> @@ -270,14 +287,21 @@ does not have a default value" field kind)))
>                                    (lambda _ '%invalid))))
>                     (define (show-default? val)
>                       (or (string? val) (number? val) (boolean? val)
> +                         (package? val)
>                           (and (symbol? val) (not (eq? val '%invalid)))
>                           (and (list? val) (and-map show-default? val))))
>  
> +                   (define (show-default val)
> +                     (cond
> +                      ((package? val)
> +                       (symbol->string (package->symbol val)))
> +                      (else (str val))))
> +
>                     `(entry (% (heading
>                                 (code ,(str field-name))
>                                 ,@(if (show-default? default)
>                                       `(" (default: "
> --8<---------------cut here---------------end--------------->8---

Cool, that looks like a better way than just getting the package name.

> Tested it to generate the new Jami service documentation, and pushed as
> commit 8e1f94421873777c6bb0b83daa4f81cbacc8b3ff.
>
> I think (guix services configuration) is starting to look good!  Thanks
> to your efforts toward improving the module.

You are welcome, happy to help out.  The next step would probably be to
automatically generate the docs when invoking ‘make docs’.

[signature.asc (application/pgp-signature, inline)]

Information forwarded to guix-patches <at> gnu.org:
bug#48934; Package guix-patches. (Tue, 03 Aug 2021 14:39:01 GMT) Full text and rfc822 format available.

Message #25 received at 48934-done <at> debbugs.gnu.org (full text, mbox):

From: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
To: Xinglu Chen <public <at> yoctocell.xyz>
Cc: 48934-done <at> debbugs.gnu.org
Subject: Re: bug#48934: [PATCH 0/2] Some improvements to (gnu services
 configuration)
Date: Tue, 03 Aug 2021 10:38:34 -0400
Hi,

Xinglu Chen <public <at> yoctocell.xyz> writes:

[...]

>> I think (guix services configuration) is starting to look good!  Thanks
>> to your efforts toward improving the module.
>
> You are welcome, happy to help out.  The next step would probably be to
> automatically generate the docs when invoking ‘make docs’.

Agreed!  I've left a TODO in the recent Jami service addition to
guix.texi that suggests to do this.  It'd be useful!

Maxim




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 01 Sep 2021 11:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 2 years and 237 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.