Patch 8.1.2225
Problem:    The "last used" info of a buffer is under used.
Solution:   Add "lastused" to getbufinfo(). List buffers sorted by last-used
            field. (Andi Massimino, closes #4722)
Files:      runtime/doc/eval.txt, runtime/doc/options.txt,
            runtime/doc/windows.txt, src/buffer.c, src/evalbuffer.c,
            src/ex_getln.c, src/misc1.c, src/option.c, src/option.h,
            src/proto/misc1.pro, src/proto/viminfo.pro,
            src/testdir/test_bufwintabinfo.vim, src/testdir/test_cmdline.vim,
            src/testdir/test_excmd.vim, src/undo.c, src/vim.h, src/viminfo.c


*** ../vim-8.1.2224/runtime/doc/eval.txt        2019-09-26 23:08:10.501926883 
+0200
--- runtime/doc/eval.txt        2019-10-27 04:59:15.196935852 +0100
***************
*** 4772,4777 ****
--- 4778,4787 ----
                        changed         TRUE if the buffer is modified.
                        changedtick     number of changes made to the buffer.
                        hidden          TRUE if the buffer is hidden.
+                       lastused        timestamp in seconds, like
+                                       |localtime()|, when the buffer was
+                                       last used.
+                                       {only with the |+viminfo| feature}
                        listed          TRUE if the buffer is listed.
                        lnum            current line number in buffer.
                        loaded          TRUE if the buffer is loaded.
*** ../vim-8.1.2224/runtime/doc/options.txt     2019-10-20 18:17:08.363431719 
+0200
--- runtime/doc/options.txt     2019-10-27 04:59:47.304750399 +0100
***************
*** 8689,8694 ****
--- 8689,8696 ----
                        complete first match.
        "list:longest"  When more than one match, list all matches and
                        complete till longest common string.
+       "list:lastused" When more than one buffer matches, sort buffers
+                       by time last used (other than the current buffer).
        When there is only a single match, it is fully completed in all cases.
  
        Examples: >
*** ../vim-8.1.2224/runtime/doc/windows.txt     2019-08-04 20:42:39.815198862 
+0200
--- runtime/doc/windows.txt     2019-10-27 04:43:57.249937909 +0100
***************
*** 1088,1093 ****
--- 1090,1096 ----
                     R   terminal buffers with a running job
                     F   terminal buffers with a finished job
                     ?   terminal buffers without a job: `:terminal NONE`
+                    t   show time last used and sort buffers
                Combining flags means they are "and"ed together, e.g.:
                     h+   hidden buffers which are modified
                     a+   active buffers which are modified
*** ../vim-8.1.2224/src/buffer.c        2019-10-01 17:01:56.342282818 +0200
--- src/buffer.c        2019-10-27 05:04:22.887244105 +0100
***************
*** 2601,2606 ****
--- 2601,2613 ----
      return match;
  }
  
+ #ifdef FEAT_VIMINFO
+ typedef struct {
+     buf_T   *buf;
+     char_u  *match;
+ } bufmatch_T;
+ #endif
+ 
  /*
   * Find all buffer names that match.
   * For command line expansion of ":buf" and ":sbuf".
***************
*** 2619,2624 ****
--- 2626,2634 ----
      char_u    *p;
      int               attempt;
      char_u    *patc;
+ #ifdef FEAT_VIMINFO
+     bufmatch_T        *matches = NULL;
+ #endif
  
      *num_file = 0;                /* return values in case of FAIL */
      *file = NULL;
***************
*** 2675,2681 ****
                            p = home_replace_save(buf, p);
                        else
                            p = vim_strsave(p);
!                       (*file)[count++] = p;
                    }
                }
            }
--- 2685,2700 ----
                            p = home_replace_save(buf, p);
                        else
                            p = vim_strsave(p);
! #ifdef FEAT_VIMINFO
!                       if (matches != NULL)
!                       {
!                           matches[count].buf = buf;
!                           matches[count].match = p;
!                           count++;
!                       }
!                       else
! #endif
!                           (*file)[count++] = p;
                    }
                }
            }
***************
*** 2691,2696 ****
--- 2710,2719 ----
                        vim_free(patc);
                    return FAIL;
                }
+ #ifdef FEAT_VIMINFO
+               if (options & WILD_BUFLASTUSED)
+                   matches = ALLOC_MULT(bufmatch_T, count);
+ #endif
            }
        }
        vim_regfree(regmatch.regprog);
***************
*** 2701,2706 ****
--- 2724,2751 ----
      if (patc != pat)
        vim_free(patc);
  
+ #ifdef FEAT_VIMINFO
+     if (matches != NULL)
+     {
+       int i;
+       if (count > 1)
+           qsort(matches, count, sizeof(bufmatch_T), buf_compare);
+       // if the current buffer is first in the list, place it at the end
+       if (matches[0].buf == curbuf)
+       {
+           for (i = 1; i < count; i++)
+               (*file)[i-1] = matches[i].match;
+           (*file)[count-1] = matches[0].match;
+       }
+       else
+       {
+           for (i = 0; i < count; i++)
+               (*file)[i] = matches[i].match;
+       }
+       vim_free(matches);
+     }
+ #endif
+ 
      *num_file = count;
      return (count == 0 ? FAIL : OK);
  }
***************
*** 3016,3022 ****
      void
  buflist_list(exarg_T *eap)
  {
!     buf_T     *buf;
      int               len;
      int               i;
      int               ro_char;
--- 3061,3067 ----
      void
  buflist_list(exarg_T *eap)
  {
!     buf_T     *buf = firstbuf;
      int               len;
      int               i;
      int               ro_char;
***************
*** 3026,3032 ****
--- 3071,3102 ----
      int               job_none_open;
  #endif
  
+ #ifdef FEAT_VIMINFO
+     garray_T  buflist;
+     buf_T     **buflist_data = NULL, **p;
+ 
+     if (vim_strchr(eap->arg, 't'))
+     {
+       ga_init2(&buflist, sizeof(buf_T *), 50);
+       for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+       {
+           if (ga_grow(&buflist, 1) == OK)
+               ((buf_T **)buflist.ga_data)[buflist.ga_len++] = buf;
+       }
+ 
+       qsort(buflist.ga_data, (size_t)buflist.ga_len,
+               sizeof(buf_T *), buf_compare);
+ 
+       p = buflist_data = (buf_T **)buflist.ga_data;
+       buf = *p;
+     }
+ 
+     for (; buf != NULL && !got_int; buf = buflist_data
+           ? (++p < buflist_data + buflist.ga_len ? *p : NULL)
+           : buf->b_next)
+ #else
      for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next)
+ #endif
      {
  #ifdef FEAT_TERMINAL
        job_running = term_job_running(buf->b_term);
***************
*** 3100,3112 ****
        do
            IObuff[len++] = ' ';
        while (--i > 0 && len < IOSIZE - 18);
!       vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len),
!               _("line %ld"), buf == curbuf ? curwin->w_cursor.lnum
                                               : (long)buflist_findlnum(buf));
        msg_outtrans(IObuff);
        out_flush();        /* output one line at a time */
        ui_breakcheck();
      }
  }
  
  /*
--- 3170,3192 ----
        do
            IObuff[len++] = ' ';
        while (--i > 0 && len < IOSIZE - 18);
! #ifdef FEAT_VIMINFO
!       if (vim_strchr(eap->arg, 't') && buf->b_last_used)
!           add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
!       else
! #endif
!           vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len),
!                   _("line %ld"), buf == curbuf ? curwin->w_cursor.lnum
                                               : (long)buflist_findlnum(buf));
        msg_outtrans(IObuff);
        out_flush();        /* output one line at a time */
        ui_breakcheck();
      }
+ 
+ #ifdef FEAT_VIMINFO
+     if (buflist_data)
+       ga_clear(&buflist);
+ #endif
  }
  
  /*
*** ../vim-8.1.2224/src/evalbuffer.c    2019-09-07 15:45:09.977228927 +0200
--- src/evalbuffer.c    2019-10-27 04:43:57.253937893 +0100
***************
*** 595,600 ****
--- 595,604 ----
      }
  #endif
  
+ #ifdef FEAT_VIMINFO
+     dict_add_number(dict, "lastused", buf->b_last_used);
+ #endif
+ 
      return dict;
  }
  
*** ../vim-8.1.2224/src/ex_getln.c      2019-10-26 20:45:11.450026374 +0200
--- src/ex_getln.c      2019-10-27 04:43:57.253937893 +0100
***************
*** 1407,1412 ****
--- 1407,1415 ----
         */
        if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm)
        {
+           int options = WILD_NO_BEEP;
+           if (wim_flags[wim_index] & WIM_BUFLASTUSED)
+               options |= WILD_BUFLASTUSED;
            if (xpc.xp_numfiles > 0)   /* typed p_wc at least twice */
            {
                /* if 'wildmode' contains "list" may still need to list */
***************
*** 1419,1428 ****
                    did_wild_list = TRUE;
                }
                if (wim_flags[wim_index] & WIM_LONGEST)
!                   res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP,
                                                               firstc != '@');
                else if (wim_flags[wim_index] & WIM_FULL)
!                   res = nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP,
                                                               firstc != '@');
                else
                    res = OK;       /* don't insert 'wildchar' now */
--- 1422,1431 ----
                    did_wild_list = TRUE;
                }
                if (wim_flags[wim_index] & WIM_LONGEST)
!                   res = nextwild(&xpc, WILD_LONGEST, options,
                                                               firstc != '@');
                else if (wim_flags[wim_index] & WIM_FULL)
!                   res = nextwild(&xpc, WILD_NEXT, options,
                                                               firstc != '@');
                else
                    res = OK;       /* don't insert 'wildchar' now */
***************
*** 1434,1443 ****
                /* if 'wildmode' first contains "longest", get longest
                 * common part */
                if (wim_flags[0] & WIM_LONGEST)
!                   res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP,
                                                               firstc != '@');
                else
!                   res = nextwild(&xpc, WILD_EXPAND_KEEP, WILD_NO_BEEP,
                                                               firstc != '@');
  
                /* if interrupted while completing, behave like it failed */
--- 1437,1446 ----
                /* if 'wildmode' first contains "longest", get longest
                 * common part */
                if (wim_flags[0] & WIM_LONGEST)
!                   res = nextwild(&xpc, WILD_LONGEST, options,
                                                               firstc != '@');
                else
!                   res = nextwild(&xpc, WILD_EXPAND_KEEP, options,
                                                               firstc != '@');
  
                /* if interrupted while completing, behave like it failed */
***************
*** 1488,1497 ****
                        redrawcmd();
                        did_wild_list = TRUE;
                        if (wim_flags[wim_index] & WIM_LONGEST)
!                           nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP,
                                                               firstc != '@');
                        else if (wim_flags[wim_index] & WIM_FULL)
!                           nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP,
                                                               firstc != '@');
                    }
                    else
--- 1491,1500 ----
                        redrawcmd();
                        did_wild_list = TRUE;
                        if (wim_flags[wim_index] & WIM_LONGEST)
!                           nextwild(&xpc, WILD_LONGEST, options,
                                                               firstc != '@');
                        else if (wim_flags[wim_index] & WIM_FULL)
!                           nextwild(&xpc, WILD_NEXT, options,
                                                               firstc != '@');
                    }
                    else
*** ../vim-8.1.2224/src/misc1.c 2019-10-17 22:58:59.066497012 +0200
--- src/misc1.c 2019-10-27 05:07:14.170365034 +0100
***************
*** 2576,2578 ****
--- 2576,2609 ----
        ;
      return path_is_url(p);
  }
+ 
+ /*
+  * Put timestamp "tt" in "buf[buflen]" in a nice format.
+  */
+     void
+ add_time(char_u *buf, size_t buflen, time_t tt)
+ {
+ #ifdef HAVE_STRFTIME
+     struct tm tmval;
+     struct tm *curtime;
+ 
+     if (vim_time() - tt >= 100)
+     {
+       curtime = vim_localtime(&tt, &tmval);
+       if (vim_time() - tt < (60L * 60L * 12L))
+           /* within 12 hours */
+           (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
+       else
+           /* longer ago */
+           (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
+     }
+     else
+ #endif
+     {
+       long seconds = (long)(vim_time() - tt);
+ 
+       vim_snprintf((char *)buf, buflen,
+               NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
+               seconds);
+     }
+ }
*** ../vim-8.1.2224/src/option.c        2019-10-19 20:57:24.956093733 +0200
--- src/option.c        2019-10-27 04:43:57.253937893 +0100
***************
*** 6987,6992 ****
--- 6987,6994 ----
            new_wim_flags[idx] |= WIM_FULL;
        else if (i == 4 && STRNCMP(p, "list", 4) == 0)
            new_wim_flags[idx] |= WIM_LIST;
+       else if (i == 8 && STRNCMP(p, "lastused", 8) == 0)
+           new_wim_flags[idx] |= WIM_BUFLASTUSED;
        else
            return FAIL;
        p += i;
*** ../vim-8.1.2224/src/option.h        2019-10-17 22:58:59.070496999 +0200
--- src/option.h        2019-10-27 04:43:57.253937893 +0100
***************
*** 338,343 ****
--- 338,344 ----
  #define WIM_FULL      0x01
  #define WIM_LONGEST   0x02
  #define WIM_LIST      0x04
+ #define WIM_BUFLASTUSED       0x08
  
  // arguments for can_bs()
  #define BS_INDENT     'i'     // "Indent"
*** ../vim-8.1.2224/src/proto/misc1.pro 2019-10-09 22:52:49.000043746 +0200
--- src/proto/misc1.pro 2019-10-27 04:43:57.253937893 +0100
***************
*** 46,49 ****
--- 46,50 ----
  char_u *get_isolated_shell_name(void);
  int path_is_url(char_u *p);
  int path_with_url(char_u *fname);
+ void add_time(char_u *buf, size_t buflen, time_t tt);
  /* vim: set ft=c : */
*** ../vim-8.1.2224/src/proto/viminfo.pro       2019-07-23 22:15:21.307518880 
+0200
--- src/proto/viminfo.pro       2019-10-27 04:43:57.253937893 +0100
***************
*** 3,7 ****
--- 3,8 ----
  void check_marks_read(void);
  int read_viminfo(char_u *file, int flags);
  void write_viminfo(char_u *file, int forceit);
+ int buf_compare(const void *s1, const void *s2);
  void ex_viminfo(exarg_T *eap);
  /* vim: set ft=c : */
*** ../vim-8.1.2224/src/testdir/test_bufwintabinfo.vim  2019-09-06 
22:45:47.574271573 +0200
--- src/testdir/test_bufwintabinfo.vim  2019-10-27 04:43:57.253937893 +0100
***************
*** 139,141 ****
--- 139,153 ----
      set foldlevel=0
    endif
  endfunc
+ 
+ function Test_getbufinfo_lastused()
+   call test_settime(1234567)
+   edit Xtestfile1
+   enew
+   call test_settime(7654321)
+   edit Xtestfile2
+   enew
+   call assert_equal(getbufinfo('Xtestfile1')[0].lastused, 1234567)
+   call assert_equal(getbufinfo('Xtestfile2')[0].lastused, 7654321)
+   call test_settime(0)
+ endfunc
*** ../vim-8.1.2224/src/testdir/test_cmdline.vim        2019-10-12 
20:17:24.605773312 +0200
--- src/testdir/test_cmdline.vim        2019-10-27 04:43:57.253937893 +0100
***************
*** 767,769 ****
--- 767,814 ----
    endtry
    bw!
  endfunc
+ 
+ func Test_buffers_lastused()
+   " check that buffers are sorted by time when wildmode has lastused
+   call test_settime(1550020000)         " middle
+   edit bufa
+   enew
+   call test_settime(1550030000)         " newest
+   edit bufb
+   enew
+   call test_settime(1550010000)         " oldest
+   edit bufc
+   enew
+   call test_settime(0)
+   enew
+ 
+   call assert_equal(['bufa', 'bufb', 'bufc'],
+       \ getcompletion('', 'buffer'))
+ 
+   let save_wildmode = &wildmode
+   set wildmode=full:lastused
+ 
+   let cap = "\<c-r>=execute('let X=getcmdline()')\<cr>"
+   call feedkeys(":b \<tab>" .. cap .. "\<esc>", 'xt')
+   call assert_equal('b bufb', X)
+   call feedkeys(":b \<tab>\<tab>" .. cap .. "\<esc>", 'xt')
+   call assert_equal('b bufa', X)
+   call feedkeys(":b \<tab>\<tab>\<tab>" .. cap .. "\<esc>", 'xt')
+   call assert_equal('b bufc', X)
+   enew
+ 
+   edit other
+   call feedkeys(":b \<tab>" .. cap .. "\<esc>", 'xt')
+   call assert_equal('b bufb', X)
+   call feedkeys(":b \<tab>\<tab>" .. cap .. "\<esc>", 'xt')
+   call assert_equal('b bufa', X)
+   call feedkeys(":b \<tab>\<tab>\<tab>" .. cap .. "\<esc>", 'xt')
+   call assert_equal('b bufc', X)
+   enew
+ 
+   let &wildmode = save_wildmode
+ 
+   bwipeout bufa
+   bwipeout bufb
+   bwipeout bufc
+ endfunc
*** ../vim-8.1.2224/src/testdir/test_excmd.vim  2019-08-06 21:29:25.742634550 
+0200
--- src/testdir/test_excmd.vim  2019-10-27 04:43:57.253937893 +0100
***************
*** 19,21 ****
--- 19,46 ----
    normal vv
    call assert_fails(":'<,'>echo 1", 'E481:')
  endfunc
+ 
+ func Test_buffers_lastused()
+   call test_settime(localtime() - 2000) " middle
+   edit bufa
+   enew
+   call test_settime(localtime() - 10)   " newest
+   edit bufb
+   enew
+   call test_settime(1550010000)               " oldest
+   edit bufc
+   enew
+   call test_settime(0)
+   enew
+ 
+   let ls = split(execute('buffers t', 'silent!'), '\n')
+   let bufs = ls->map({i,v->split(v, '"\s*')[1:2]})
+   call assert_equal(['bufb', 'bufa', 'bufc'], bufs[1:]->map({i,v->v[0]}))
+   call assert_match('1[0-3] seconds ago', bufs[1][1])
+   call assert_match('\d\d:\d\d:\d\d', bufs[2][1])
+   call assert_match('2019/02/1\d \d\d:\d\d:00', bufs[3][1])
+ 
+   bwipeout bufa
+   bwipeout bufb
+   bwipeout bufc
+ endfunc
*** ../vim-8.1.2224/src/undo.c  2019-09-21 23:09:00.979830687 +0200
--- src/undo.c  2019-10-27 04:43:57.253937893 +0100
***************
*** 106,112 ****
  static void u_doit(int count);
  static void u_undoredo(int undo);
  static void u_undo_end(int did_undo, int absolute);
- static void u_add_time(char_u *buf, size_t buflen, time_t tt);
  static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
  static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
  static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
--- 106,111 ----
***************
*** 2973,2979 ****
      if (uhp == NULL)
        *msgbuf = NUL;
      else
!       u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
  
  #ifdef FEAT_CONCEAL
      {
--- 2972,2978 ----
      if (uhp == NULL)
        *msgbuf = NUL;
      else
!       add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
  
  #ifdef FEAT_CONCEAL
      {
***************
*** 3050,3056 ****
                break;
            vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d  ",
                                                        uhp->uh_seq, changes);
!           u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
                                                                uhp->uh_time);
            if (uhp->uh_save_nr > 0)
            {
--- 3049,3055 ----
                break;
            vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d  ",
                                                        uhp->uh_seq, changes);
!           add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
                                                                uhp->uh_time);
            if (uhp->uh_save_nr > 0)
            {
***************
*** 3124,3160 ****
      }
  }
  
- /*
-  * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
-  */
-     static void
- u_add_time(char_u *buf, size_t buflen, time_t tt)
- {
- #ifdef HAVE_STRFTIME
-     struct tm tmval;
-     struct tm *curtime;
- 
-     if (vim_time() - tt >= 100)
-     {
-       curtime = vim_localtime(&tt, &tmval);
-       if (vim_time() - tt < (60L * 60L * 12L))
-           /* within 12 hours */
-           (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
-       else
-           /* longer ago */
-           (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
-     }
-     else
- #endif
-     {
-       long seconds = (long)(vim_time() - tt);
- 
-       vim_snprintf((char *)buf, buflen,
-               NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
-               seconds);
-     }
- }
- 
  /*
   * ":undojoin": continue adding to the last entry list
   */
--- 3123,3128 ----
*** ../vim-8.1.2224/src/vim.h   2019-10-26 16:21:34.511468348 +0200
--- src/vim.h   2019-10-27 04:45:59.721441102 +0100
***************
*** 811,816 ****
--- 811,817 ----
  #define WILD_ALLLINKS             0x200
  #define WILD_IGNORE_COMPLETESLASH   0x400
  #define WILD_NOERROR              0x800  // sets EW_NOERROR
+ #define WILD_BUFLASTUSED          0x1000
  
  // Flags for expand_wildcards()
  #define EW_DIR                0x01    // include directory names
*** ../vim-8.1.2224/src/viminfo.c       2019-10-09 22:01:20.599438001 +0200
--- src/viminfo.c       2019-10-27 04:43:57.257937875 +0100
***************
*** 2152,2158 ****
  /*
   * Compare functions for qsort() below, that compares b_last_used.
   */
!     static int
  buf_compare(const void *s1, const void *s2)
  {
      buf_T *buf1 = *(buf_T **)s1;
--- 2152,2158 ----
  /*
   * Compare functions for qsort() below, that compares b_last_used.
   */
!     int
  buf_compare(const void *s1, const void *s2)
  {
      buf_T *buf1 = *(buf_T **)s1;
*** ../vim-8.1.2224/src/version.c       2019-10-26 21:33:11.334329088 +0200
--- src/version.c       2019-10-27 04:53:51.290982776 +0100
***************
*** 743,744 ****
--- 743,746 ----
  {   /* Add new patch number below this line */
+ /**/
+     2225,
  /**/

-- 
Bad fashion can discourage normal people from interacting with the engineer
and talking about the cute things their children do.
                                (Scott Adams - The Dilbert principle)

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/201910270413.x9R4DmXp018194%40masaka.moolenaar.net.

Raspunde prin e-mail lui