Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: PATCH: source file info from PS4



Thanks!

This is certainly something I've wanted ever since funcstack was improved but had been holding back on.

The fretting about what letter to use after % would be reduced and if the promptsubst option also applied to PS4. In my opinion not only is it more readable and requires less memory on a users part, but is also more flexible.


On Tue, Sep 16, 2008 at 10:50 AM, Peter Stephenson <pws@xxxxxxx> wrote:
Now we have logic for finding the source file and corresponding line
number of executed code, this adds the prompt escapes %x and %I which
are like %N and %i but for the file where the code was defined.  %x isn't
ideal but upper and lower case %s, %f and %n are all used.  It stands
for "execution file", or something.  The idea is that you set
PS4='+%x:%I>'

While doing this, I spotted that we could improve the information
available to funcstack and the interface to doshfunc() by passing in a
Shfunc instead of an Eprog.  This is a *much* cleaner interface.  Now
the funcstack entry is guaranteed to get the details of the shell
function correct.

Index: Doc/Zsh/prompt.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/prompt.yo,v
retrieving revision 1.15
diff -u -r1.15 prompt.yo
--- Doc/Zsh/prompt.yo   24 Jun 2008 08:44:16 -0000      1.15
+++ Doc/Zsh/prompt.yo   16 Sep 2008 14:41:10 -0000
@@ -113,6 +113,11 @@
 shell function given by tt(%N).  This is most useful for debugging as part
 of tt($PS4).
 )
+item(tt(%I))(
+The line number currently being executed in the file tt(%x).  This is
+similar to tt(%i), but the line number is always a line number in the
+file where the code was defined, even if the code is a shell function.
+)
 item(tt(%j))(
 The number of jobs.
 )
@@ -126,6 +131,11 @@
 the `tt(%)' to specify a number of trailing path components to show; zero
 means the full path.  A negative integer specifies leading components.
 )
+item(tt(%x))(
+The name of the file containing the source code currently being
+executed.  This behaves as tt(%N) except that function and eval command
+names are not shown, instead the file where they were defined.
+)
 xitem(tt(%c))
 xitem(tt(%.))
 item(tt(%C))(
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.151
diff -u -r1.151 exec.c
--- Src/exec.c  11 Sep 2008 17:14:39 -0000      1.151
+++ Src/exec.c  16 Sep 2008 14:41:10 -0000
@@ -518,7 +518,7 @@
       return 127;

    pushnode(args, arg0);
-    return doshfunc(shf->node.nam, shf->funcdef, args, shf->node.flags, 1);
+    return doshfunc(shf, args, shf->node.flags, 1);
 }

 /* execute an external command */
@@ -4064,7 +4064,7 @@
    cmdsp = 0;
    if ((osfc = sfcontext) == SFC_NONE)
       sfcontext = SFC_DIRECT;
-    doshfunc(shf->node.nam, shf->funcdef, args, shf->node.flags, 0);
+    doshfunc(shf, args, shf->node.flags, 0);
    sfcontext = osfc;
    free(cmdstack);
    cmdstack = ocs;
@@ -4200,18 +4200,20 @@

 /**/
 mod_export int
-doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
+doshfunc(Shfunc shfunc, LinkList doshargs, int flags, int noreturnval)
 {
    char **tab, **x, *oargv0;
    int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret;
    int *oldpipestats = NULL;
-    char saveopts[OPT_SIZE], *oldscriptname = scriptname, *fname = dupstring(name);
+    char saveopts[OPT_SIZE], *oldscriptname = scriptname;
+    char *name = shfunc->node.nam;
+    char *fname = dupstring(name);
    int obreaks, saveemulation ;
+    Eprog prog;
    struct funcstack fstack;
 #ifdef MAX_FUNCTION_DEPTH
    static int funcdepth;
 #endif
-    Shfunc shf;

    pushheap();

@@ -4291,14 +4293,10 @@
    fstack.tp = FS_FUNC;
    funcstack = &fstack;

-    if ((shf = (Shfunc) shfunctab->getnode(shfunctab, name))) {
-       fstack.flineno = shf->lineno;
-       fstack.filename = dupstring(shf->filename);
-    } else {
-       fstack.flineno = 0;
-       fstack.filename = dupstring(fstack.caller);
-    }
+    fstack.flineno = shfunc->lineno;
+    fstack.filename = dupstring(shfunc->filename);

+    prog = shfunc->funcdef;
    if (prog->flags & EF_RUN) {
       Shfunc shf;

Index: Src/init.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/init.c,v
retrieving revision 1.96
diff -u -r1.96 init.c
--- Src/init.c  11 Sep 2008 17:14:39 -0000      1.96
+++ Src/init.c  16 Sep 2008 14:41:10 -0000
@@ -149,7 +149,7 @@
           int toksav = tok;

           if (toplevel &&
-               (getshfunc("preexec") != &dummy_eprog ||
+               (getshfunc("preexec") ||
                paramtab->getnode(paramtab, "preexec_functions"))) {
               LinkList args;
               char *cmdstr;
Index: Src/math.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/math.c,v
retrieving revision 1.33
diff -u -r1.33 math.c
--- Src/math.c  12 Jun 2008 13:45:06 -0000      1.33
+++ Src/math.c  16 Sep 2008 14:41:10 -0000
@@ -868,11 +868,11 @@
                                          argc <= f->maxargs)) {
                   if (f->flags & MFF_USERFUNC) {
                       char *shfnam = f->module ? f->module : n;
-                       Eprog prog = getshfunc(shfnam);
-                       if (prog == &dummy_eprog)
+                       Shfunc shfunc = getshfunc(shfnam);
+                       if (!shfunc)
                           zerr("no such function: %s", shfnam);
                       else {
-                           doshfunc(n, prog, l, 0, 1);
+                           doshfunc(shfunc, l, 0, 1);
                           return lastmathval;
                       }
                   } else {
Index: Src/prompt.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/prompt.c,v
retrieving revision 1.53
diff -u -r1.53 prompt.c
--- Src/prompt.c        15 Sep 2008 16:18:06 -0000      1.53
+++ Src/prompt.c        16 Sep 2008 14:41:11 -0000
@@ -725,11 +725,37 @@
               if(Rstring)
                   stradd(Rstring);
               break;
+           case 'I':
+               if (funcstack && funcstack->tp != FS_SOURCE) {
+                   /*
+                    * We're in a function or an eval with
+                    * EVALLINENO.  Calculate the line number in
+                    * the file.
+                    */
+                   zlong flineno = lineno + funcstack->flineno;
+                   /* take account of eval line nos. starting at 1 */
+                   if (funcstack->tp == FS_EVAL)
+                       lineno--;
+                   addbufspc(DIGBUFSIZE);
+                   sprintf(bp, "%ld", (long)flineno);
+                   bp += strlen(bp);
+                   break;
+               }
+               /* else we're in a file and lineno is already correct */
+               /* FALLTHROUGH */
           case 'i':
               addbufspc(DIGBUFSIZE);
               sprintf(bp, "%ld", (long)lineno);
               bp += strlen(bp);
               break;
+           case 'x':
+               if (funcstack && funcstack->tp != FS_SOURCE)
+                   promptpath(funcstack->filename ? funcstack->filename : "",
+                              arg, 0);
+               else
+                   promptpath(scriptfilename ? scriptfilename : argzero,
+                              arg, 0);
+               break;
           case '\0':
               return 0;
           case Meta:
Index: Src/signals.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/signals.c,v
retrieving revision 1.50
diff -u -r1.50 signals.c
--- Src/signals.c       11 Aug 2008 19:22:54 -0000      1.50
+++ Src/signals.c       16 Sep 2008 14:41:11 -0000
@@ -963,8 +963,7 @@
    }

    if (exittr) {
-       dotrapargs(SIGEXIT, &exittr, (exittr & ZSIG_FUNC) ?
-                  ((Shfunc)exitfn)->funcdef : (Eprog) exitfn);
+       dotrapargs(SIGEXIT, &exittr, exitfn);
       if (exittr & ZSIG_FUNC)
           shfunctab->freenode((HashNode)exitfn);
       else
@@ -1077,8 +1076,16 @@
 /**/
 int trapisfunc;

+/*
+ * sig is the signal number.
+ * *sigtr is the value to be taken as the field in sigtrapped (since
+ *   that may have changed by this point if we are exiting).
+ * sigfn is an Eprog with a non-function eval list, or a Shfunc
+ *   with a function trap.  It may be NULL with an ignored signal.
+ */
+
 /**/
-void
+static void
 dotrapargs(int sig, int *sigtr, void *sigfn)
 {
    LinkList args;
@@ -1153,7 +1160,7 @@
       trapisfunc = isfunc = 1;

       sfcontext = SFC_SIGNAL;
-       doshfunc(name, sigfn, args, 0, 1);
+       doshfunc((Shfunc)sigfn, args, 0, 1);
       sfcontext = osc;
       freelinklist(args, (FreeFunc) NULL);
       zsfree(name);
@@ -1162,7 +1169,7 @@
       trap_state = TRAP_STATE_PRIMED;
       trapisfunc = isfunc = 0;

-       execode(sigfn, 1, 0);
+       execode((Eprog)sigfn, 1, 0);
    }
    runhookdef(AFTERTRAPHOOK, NULL);

@@ -1215,12 +1222,12 @@
 void
 dotrap(int sig)
 {
-    Eprog funcprog;
+    void *funcprog;

    if (sigtrapped[sig] & ZSIG_FUNC) {
       HashNode hn = gettrapnode(sig, 0);
       if (hn)
-           funcprog = ((Shfunc)hn)->funcdef;
+           funcprog = hn;
       else {
 #ifdef DEBUG
           dputs("BUG: running function trap which has escaped.");
@@ -1230,7 +1237,11 @@
    } else
       funcprog = siglists[sig];

-    /* Copied from dotrapargs(). */
+    /*
+     * Copied from dotrapargs().
+     * (In fact, the gain from duplicating this appears to be virtually
+     * zero.  Not sure why it's here.)
+     */
    if ((sigtrapped[sig] & ZSIG_IGNORED) || !funcprog || errflag)
       return;

Index: Src/utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/utils.c,v
retrieving revision 1.199
diff -u -r1.199 utils.c
--- Src/utils.c 11 Aug 2008 19:22:54 -0000      1.199
+++ Src/utils.c 16 Sep 2008 14:41:11 -0000
@@ -35,6 +35,8 @@
 /**/
 mod_export char *scriptname;     /* is sometimes a function name */

+/* filename of script or other file containing code source e.g. autoload */
+
 /**/
 mod_export char *scriptfilename;

@@ -1134,7 +1136,7 @@
 mod_export int
 callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
 {
-    Eprog prog;
+    Shfunc shfunc;
       /*
        * Save stopmsg, since user doesn't get a chance to respond
        * to a list of jobs generated in a hook.
@@ -1143,8 +1145,8 @@

    sfcontext = SFC_HOOK;

-    if ((prog = getshfunc(name)) != &dummy_eprog) {
-       ret = doshfunc(name, prog, lnklst, 0, 1);
+    if ((shfunc = getshfunc(name))) {
+       ret = doshfunc(shfunc, lnklst, 0, 1);
       stat = 0;
    }

@@ -1159,8 +1161,8 @@

       if ((arrptr = getaparam(arrnam))) {
           for (; *arrptr; arrptr++) {
-               if ((prog = getshfunc(*arrptr)) != &dummy_eprog) {
-                   int newret = doshfunc(arrnam, prog, lnklst, 0, 1);
+               if ((shfunc = getshfunc(*arrptr))) {
+                   int newret = doshfunc(shfunc, lnklst, 0, 1);
                   if (!ret)
                       ret = newret;
                   stat = 0;
@@ -2893,15 +2895,10 @@
 /* Get the definition of a shell function */

 /**/
-mod_export Eprog
+mod_export Shfunc
 getshfunc(char *nam)
 {
-    Shfunc shf;
-
-    if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, nam)))
-       return &dummy_eprog;
-
-    return shf->funcdef;
+    return (Shfunc) shfunctab->getnode(shfunctab, nam);
 }

 /**/
Index: Src/Modules/zftp.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/zftp.c,v
retrieving revision 1.48
diff -u -r1.48 zftp.c
--- Src/Modules/zftp.c  4 Sep 2008 22:23:52 -0000       1.48
+++ Src/Modules/zftp.c  16 Sep 2008 14:41:11 -0000
@@ -1469,9 +1469,9 @@
    char lsbuf[ZF_BUFSIZE], *ascbuf = NULL, *optr;
    off_t sofar = 0, last_sofar = 0;
    readwrite_t read_ptr = zfread, write_ptr = zfwrite;
-    Eprog prog;
+    Shfunc shfunc;

-    if (progress && (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+    if (progress && (shfunc = getshfunc("zftp_progress"))) {
       /*
        * progress to set up:  ZFTP_COUNT is zero.
        * We do this here in case we needed to wait for a RETR
@@ -1480,7 +1480,7 @@
       int osc = sfcontext;

       sfcontext = SFC_HOOK;
-       doshfunc("zftp_progress", prog, NULL, 0, 1);
+       doshfunc(shfunc, NULL, 0, 1);
       sfcontext = osc;
       /* Now add in the bit of the file we've got/sent already */
       sofar = last_sofar = startat;
@@ -1608,12 +1608,12 @@
       } else
           break;
       if (!ret && sofar != last_sofar && progress &&
-           (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+           (shfunc = getshfunc("zftp_progress"))) {
           int osc = sfcontext;

           zfsetparam("ZFTP_COUNT", &sofar, ZFPM_READONLY|ZFPM_INTEGER);
           sfcontext = SFC_HOOK;
-           doshfunc("zftp_progress", prog, NULL, 0, 1);
+           doshfunc(shfunc, NULL, 0, 1);
           sfcontext = osc;
           last_sofar = sofar;
       }
@@ -2364,7 +2364,7 @@
 {
    char *ptr, *eptr;
    int endc;
-    Eprog prog;
+    Shfunc shfunc;

    if (zfprefs & ZFPF_DUMB)
       return 1;
@@ -2391,11 +2391,11 @@
     * front end.  By putting it here, and in close when ZFTP_PWD is unset,
     * we at least cover the bases.
     */
-    if ((prog = getshfunc("zftp_chpwd")) != &dummy_eprog) {
+    if ((shfunc = getshfunc("zftp_chpwd"))) {
       int osc = sfcontext;

       sfcontext = SFC_HOOK;
-       doshfunc("zftp_chpwd", prog, NULL, 0, 1);
+       doshfunc(shfunc, NULL, 0, 1);
       sfcontext = osc;
    }
    return 0;
@@ -2549,7 +2549,7 @@
 {
    int ret = 0, recv = (flags & ZFTP_RECV), getsize = 0, progress = 1;
    char *cmd = recv ? "RETR " : (flags & ZFTP_APPE) ? "APPE " : "STOR ";
-    Eprog prog;
+    Shfunc shfunc;

    /*
     * At this point I'd like to set progress to 0 if we're
@@ -2567,7 +2567,7 @@
    for (; *args; args++) {
       char *ln, *rest = NULL;
       off_t startat = 0;
-       if (progress && (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+       if (progress && (shfunc = getshfunc("zftp_progress"))) {
           off_t sz = -1;
           /*
            * This calls the SIZE command to get the size for remote
@@ -2608,14 +2608,14 @@
        * if and only if we called zfsenddata();
        */
       if (progress && ret != 2 &&
-           (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+           (shfunc = getshfunc("zftp_progress"))) {
           /* progress to finish: ZFTP_TRANSFER set to GF or PF */
           int osc = sfcontext;

           zfsetparam("ZFTP_TRANSFER", ztrdup(recv ? "GF" : "PF"),
                      ZFPM_READONLY);
           sfcontext = SFC_HOOK;
-           doshfunc("zftp_progress", prog, NULL, 0, 1);
+           doshfunc(shfunc, NULL, 0, 1);
           sfcontext = osc;
       }
       if (rest) {
@@ -2715,7 +2715,7 @@
 zfclose(int leaveparams)
 {
    char **aptr;
-    Eprog prog;
+    Shfunc shfunc;

    if (!zfsess->control)
       return;
@@ -2766,11 +2766,11 @@
           zfunsetparam(*aptr);

       /* Now ZFTP_PWD is unset.  It's up to zftp_chpwd to notice. */
-       if ((prog = getshfunc("zftp_chpwd")) != &dummy_eprog) {
+       if ((shfunc = getshfunc("zftp_chpwd"))) {
           int osc = sfcontext;

           sfcontext = SFC_HOOK;
-           doshfunc("zftp_chpwd", prog, NULL, 0, 1);
+           doshfunc(shfunc, NULL, 0, 1);
           sfcontext = osc;
       }
    }
Index: Src/Zle/compcore.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/compcore.c,v
retrieving revision 1.96
diff -u -r1.96 compcore.c
--- Src/Zle/compcore.c  7 Jul 2008 08:33:28 -0000       1.96
+++ Src/Zle/compcore.c  16 Sep 2008 14:41:12 -0000
@@ -540,13 +540,13 @@
 static void
 callcompfunc(char *s, char *fn)
 {
-    Eprog prog;
+    Shfunc shfunc;
    int lv = lastval;
    char buf[20];

    METACHECK();

-    if ((prog = getshfunc(fn)) != &dummy_eprog) {
+    if ((shfunc = getshfunc(fn))) {
       char **p, *tmp;
       int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
       unsigned int rset, kset;
@@ -814,7 +814,7 @@
               while (*p)
                   addlinknode(largs, dupstring(*p++));
           }
-           doshfunc(fn, prog, largs, 0, 0);
+           doshfunc(shfunc, largs, 0, 0);
           cfret = lastval;
           lastval = olv;
       } OLDHEAPS;
Index: Src/Zle/compctl.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/compctl.c,v
retrieving revision 1.35
diff -u -r1.35 compctl.c
--- Src/Zle/compctl.c   3 Oct 2007 16:18:38 -0000       1.35
+++ Src/Zle/compctl.c   16 Sep 2008 14:41:12 -0000
@@ -3635,12 +3635,12 @@
    }
    if (cc->func) {
       /* This handles the compctl -K flag. */
-       Eprog prog;
+       Shfunc shfunc;
       char **r;
       int lv = lastval;

       /* Get the function. */
-       if ((prog = getshfunc(cc->func)) != &dummy_eprog) {
+       if ((shfunc = getshfunc(cc->func))) {
           /* We have it, so build a argument list. */
           LinkList args = newlinklist();
           int osc = sfcontext;
@@ -3664,7 +3664,7 @@
               incompctlfunc = 1;
           sfcontext = SFC_COMPLETE;
           /* Call the function. */
-           doshfunc(cc->func, prog, args, 0, 1);
+           doshfunc(shfunc, args, 0, 1);
           sfcontext = osc;
           incompctlfunc = 0;
           /* And get the result from the reply parameter. */
@@ -3809,12 +3809,12 @@
       /* generate the user-defined display list: if anything fails, *
        * we silently allow the normal completion list to be used.   */
       char **yaptr = NULL, *uv = NULL;
-       Eprog prog;
+       Shfunc shfunc;

       if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
           /* from variable */
           uv = cc->ylist + (cc->ylist[0] == '$');
-       } else if ((prog = getshfunc(cc->ylist)) != &dummy_eprog) {
+       } else if ((shfunc = getshfunc(cc->ylist))) {
           /* from function:  pass completions as arg list */
           LinkList args = newlinklist();
           LinkNode ln;
@@ -3839,7 +3839,7 @@
           if (incompfunc != 1)
               incompctlfunc = 1;
           sfcontext = SFC_COMPLETE;
-           doshfunc(cc->ylist, prog, args, 0, 1);
+           doshfunc(shfunc, args, 0, 1);
           sfcontext = osc;
           incompctlfunc = 0;
           uv = "reply";
Index: Src/Zle/zle_main.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_main.c,v
retrieving revision 1.115
diff -u -r1.115 zle_main.c
--- Src/Zle/zle_main.c  8 Sep 2008 06:24:23 -0000       1.115
+++ Src/Zle/zle_main.c  16 Sep 2008 14:41:12 -0000
@@ -1304,9 +1304,8 @@
       r = 1;
    } else {
       Shfunc shf = (Shfunc) shfunctab->getnode(shfunctab, w->u.fnnam);
-       Eprog prog = (shf ? shf->funcdef : &dummy_eprog);

-       if(prog == &dummy_eprog) {
+       if (!shf) {
           /* the shell function doesn't exist */
           char *nm = nicedup(w->u.fnnam, 0);
           char *msg = tricat("No such shell function `", nm, "'");
@@ -1330,7 +1329,7 @@
           makezleparams(0);
           sfcontext = SFC_WIDGET;
           opts[XTRACE] = 0;
-           ret = doshfunc(w->u.fnnam, prog, largs, shf->node.flags, 1);
+           ret = doshfunc(shf, largs, shf->node.flags, 1);
           opts[XTRACE] = oxt;
           sfcontext = osc;
           endparamscope();
Index: Src/Zle/zle_misc.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_misc.c,v
retrieving revision 1.54
diff -u -r1.54 zle_misc.c
--- Src/Zle/zle_misc.c  4 May 2008 18:30:04 -0000       1.54
+++ Src/Zle/zle_misc.c  16 Sep 2008 14:41:12 -0000
@@ -1358,9 +1358,9 @@
 iremovesuffix(ZLE_INT_T c, int keep)
 {
    if (suffixfunc) {
-       Eprog prog = getshfunc(suffixfunc);
+       Shfunc shfunc = getshfunc(suffixfunc);

-       if (prog != &dummy_eprog) {
+       if (shfunc) {
           LinkList args = newlinklist();
           char buf[20];
           int osc = sfcontext;
@@ -1384,7 +1384,7 @@
           startparamscope();
           makezleparams(0);
           sfcontext = SFC_COMPLETE;
-           doshfunc(suffixfunc, prog, args, 0, 1);
+           doshfunc(shfunc, args, 0, 1);
           sfcontext = osc;
           endparamscope();

Index: Test/E02xtrace.ztst
===================================================================
RCS file: /cvsroot/zsh/zsh/Test/E02xtrace.ztst,v
retrieving revision 1.7
diff -u -r1.7 E02xtrace.ztst
--- Test/E02xtrace.ztst 11 Aug 2008 08:40:58 -0000      1.7
+++ Test/E02xtrace.ztst 16 Sep 2008 14:41:12 -0000
@@ -90,3 +90,18 @@
 >Tracing: function
 ?+xtf:1> local regression_test_dummy_variable
 ?+xtf:2> print 'Tracing: function'
+
+ echo 'PS4="+%x:%I> "
+ fn() {
+   print This is fn.
+ }
+ :
+ fn
+ ' >fnfile
+ $ZTST_testdir/../Src/zsh -fx ./fnfile
+0:Trace output with sourcefile and line number.
+>This is fn.
+?+./fnfile:1> PS4='+%x:%I> '
+?+./fnfile:5> :
+?+./fnfile:6> fn
+?+./fnfile:3> print This is fn.


--
Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070



Messages sorted by: Reverse Date, Date, Thread, Author