Re: building on macos

2025-01-28 Thread Jean-Christophe Helary
Regarding the dependency on llvm-16, I get that during the build process with 
llvm-10:

(cd src; make)
/opt/local/libexec/llvm-10/bin/llc: error: /opt/local/libexec/llvm-10/bin/llc: 
picolisp.bc: error: Invalid value (Producer: 'LLVM16.0.6' Reader: 'LLVM 10.0.1')
make: *** [../bin/picolisp] Error 1

Jean-Christophe

> On Jan 12, 2025, at 11:42, Jean-Christophe Helary 
>  wrote:
> 
> Hello everyone!
> 
> Some updates that need confirmation.
> 
> https://picolisp.com/wiki/?alternativeMacOSRepository
> 
> It looks like picolisp now requires llvm-16.
> 
> Also, for a reason that escapes me (maybe a bug on the part of the MacPort 
> team, but I need to investigate), port installs llvm-16 binaries with the 
> -mp-16 suffix *but* does not create aliases to the non-suffixed version.
> 
> That generates a number of “command not found” errors, namely:
> - llvm-config
> - opt
> - llvm-link
> - llc
> 
> I’ll check that with the MacPort teams before proposing a fix. But it’s weird 
> because the non-suffixed aliases seemed to have been produced in previous 
> versions of the llvm port.
> 
> et voilà !
> 
> Jean-Christophe
> 
>> On Aug 14, 2023, at 22:36, l@tlo  wrote:
>> 
>> 
>> 
>>> On Aug 14, 2023, at 15:15, l@tlo  wrote:
>>> 
>>> One year later...
>>> 
>>> (I don't even remember sending that older mail... 😅)
>>> 
>>> 
>>> Building on macOS (13) seems to work fine with the following instructions:
>>> 
>>> https://picolisp.com/wiki/?alternativeMacOSRepository
>> 
>> I just noticed the reply by Louis Abraham:
>> 
>> https://picolisp.com/wiki/-A725.html
>> 
>> For readability it might be better to merge the two documents into one: the 
>> brew style and the macport style.
>> 
>> Louis, you seem to mean that your version works with M1 machines. Have you 
>> tried it on Intel machines?
>> 
>> JC
>> 
>>> 
>>> *and*
>>> 
>>> Mike's Makefile here:
>>> 
>>> https://git.envs.net/mpech/pil21-tests/raw/branch/master/Makefile.macos
>>> 
>>> 
>>> One question to Mike:
>>> 
>>> Would it be possible that you merge your makefile with Alex's most recent 
>>> and add conditionals to check whether the system is macOS?
>>> 
>>> That way, we would not need any special instructions to build on macOS :)
>>> 
>>> 
>>> If not, I'll update the instructions and will add a link to your Makefile.
>>> 
>>> 
>>> And then, Mia's blog article could be updated to reflect the current status 
>>> :)
>>> 
>>> https://picolisp-explored.com/how-to-install-picolisp
>>> 
>>> 
>>> Jean-Christophe
>>> 
 On May 30, 2022, at 13:39, Jean-Christophe Helary 
  wrote:
 
 
 
> On May 30, 2022, at 18:18, Alexander Burger  wrote:
> 
> Hi Jean-Christophe,
> 
>> It looks like I'm slowly getting somewhere...
>> ...
> 
> Great! :)
> 
> 
>> ./pil: line 2: exec: 
>> /Users/suzume/Documents/Repositories/pil21+/src/../bin/picolisp: cannot 
>> execute: No such file or directory
> 
> That's an easy one. It tries to bootstrap, but bin/picolisp does not 
> exist yet.
> For that reason the pre-build *.ll files are included in the distro. Just
> (re)install these.
 
 Yes, but now, I'm back to my readline errors...
 
 ➜  pil21+ git:(master) ✗ (cd src; make)
 clang -O3 -w -c -o lib.bc -D_OS='"Darwin"' -D_CPU='"x86_64"' `pkg-config 
 --cflags libffi` -emit-llvm lib.c
 lib.c:119:4: error: use of undeclared identifier 'rl_catch_signals'
 rl_catch_signals = 0;
 ^
 lib.c:121:4: error: use of undeclared identifier 'rl_input_available_hook'
 rl_input_available_hook = rlAvail;
 ^
 
 (etc.)
 
 I'm using this in .profile:
 
 ## libffi
 export 
 PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig:$PKG_CONFIG_PATH"
 export LDFLAGS="-L/usr/local/opt/libffi/lib:$LDFLAGS"
 export CPPFLAGS="-I/usr/local/opt/libffi/include:$CPPFLAGS"
 
 ## openssl
 export PATH="/usr/local/opt/openssl@3/bin:$PATH"
 export LDFLAGS="-L/usr/local/opt/openssl@3/lib:$LDFLAGS"
 export CPPFLAGS="-I/usr/local/opt/openssl@3/include:$CPPFLAGS"
 
 ## readline
 export 
 PKG_CONFIG_PATH="/usr/local/opt/readline/lib/pkgconfig:$PKG_CONFIG_PATH"
 export LDFLAGS="-L/usr/local/opt/readline/lib:$LDFLAGS"
 export CPPFLAGS="-I/usr/local/opt/readline/include:$CPPFLAGS"
 
 ## llvm
 export PATH="/usr/local/opt/llvm@11/bin:$PATH"
 export LDFLAGS="-L/usr/local/opt/llvm@11/lib:$LDFLAGS"
 export CPPFLAGS="-I/usr/local/opt/llvm@11/include:$CPPFLAGS"
 
 JC :(
> 
> 
> -- 
> Jean-Christophe Helary
> https://sr.ht/~brandelune/
> @jchelary@sciences.social
> 

-- 
Jean-Christophe Helary
https://sr.ht/~brandelune/
@jchelary@sciences.social



--
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


How to use enum? to check for existence in sparse array

2025-01-28 Thread Lindsay Lawrence
Hi Alex,

Is this a bug or is my understanding of how to use  enum? from the
documentation not correct.

I have a little project where I would like to use 'enum. I was using 'idx
before, but 'enum looks like a much better fit. My current  problem is that
in a sparse array I am getting false positives as to whether a node exists
when checking with enum?

How do I use 'enum? to check for entry existence? Using the (val (enum
..)) and checking for NIL  also modifies the tree does it not. It is also
much slower than just 'enum?

Regards,
/Lindsay

# ()

# Generate a 'Hash' value (based on Lehmer pseudo-random algo

(de Hasher (Val Seed)
   (default Seed 7337)
   (let (State Seed  Lst (if (lst? Val) Val (list Val)))
  (for C Lst
 (setq State (% (* (* C State) 279470273) 4294967291)) ) ) )

# Generate some hash values
(de TestEnum (N)
   (off *HT)
   (for I N
  (let (Val (Hasher I))
 (if (enum? '*HT Val)
(prinl "! Duplicate: " I "; " Val "; " (enum? '*HT Val))
(set (enum '*HT Val) I)
(prinl "+ Added: " I "; " Val "; " (enum? '*HT Val)) ) ) ) )
# ()

# Why does enum? report 'duplicate' hash value

: (TestEnum (** 2 3))
+ Added: 1; 1773995194;
! Duplicate: 2; 3547990388; 1
+ Added: 3; 1027018291;
+ Added: 4; 2801013485;
+ Added: 5; 280041388;
! Duplicate: 6; 2054036582; 3
+ Added: 7; 3828031776;
+ Added: 8; 1307059679;
-> NIL

# The hash values are all unique

: (for C (** 2 3) (printsp C (Hasher C)) (prinl) )
1 1773995194
2 3547990388
3 1027018291
4 2801013485
5 280041388
6 2054036582
7 3828031776
8 1307059679
-> NIL

# 'enum shows TestEnum didn't add the value, but also creates it doesn't it?

: (for C (** 2 3) (println (val (enum '*HT (Hasher C)
1
NIL
3
4
5
NIL
7
8
-> 8


P.S. For the integers between 1 and (2 ** 20) (the extent of my testing so
far) the Hasher functions generates unique values that are nicely
distributed and work well in an 'idx tree. As 'enum seems much faster (and
simpler) than 'idx,   I would like to use that, with an 'assoc list in the
value for any potential collisions. Note: the hasher currently handles
lists of numbers as well.


Re: How to use enum? to check for existence in sparse array

2025-01-28 Thread Lindsay Lawrence
> Is this a bug or is my understanding of how to use  enum? from the
> documentation not correct.
>
>  My mistake!

The correct syntax is (enum? *HT Val) and not (enum? '*HT Val).
I was quoting the tree name. Doh!

Regards,
Lindsay

Note: I am still not entirely clear why 'enum? can return unexpected (at
least to me) results in the sparse array case (see below)
Is it because 'enum precreates key nodes in the tree for a sparse array in
order to keep it balanced?.

e.g.

: (pp 'TestEnum)
(de TestEnum (N)
   (off *HT)
   (for I N
  (let (Val (Hasher I))
 (if (enum? *HT Val)
(prinl
   "! Duplicate: "
   I
   "; "
   Val
   "; "
   (sym (enum? '*HT Val))
   "; "
   (sym (val (enum '*HT Val))) )
(set (enum '*HT Val) I) ) ) ) )
-> TestEnum

: (TestEnum (** 2 16))
! Duplicate: 21487; 25853; NIL; NIL
! Duplicate: 42974; 51706; (NIL (NIL NIL NIL (NIL NIL NIL (NIL (NIL (NIL
(NIL (NIL (NIL NIL NIL (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL
11011 NIL NIL NIL (NIL (NIL NIL NIL NIL NIL NIL NIL (NIL (NIL NIL
NIL NIL NIL NIL NIL NIL NIL (NIL NIL 28854)) NIL (NIL NIL NIL NIL NIL
NIL NIL NIL NIL (NIL (NIL (NIL NIL NIL (NIL NIL NIL NIL NIL NIL 3644));
NIL
! Duplicate: 50121; 260192; NIL; NIL
! Duplicate: 64461; 77559; NIL; NIL
-> 65536




>
>


Re: How to use enum? to check for existence in sparse array

2025-01-28 Thread Lindsay Lawrence
>
> Note: I am still not entirely clear why 'enum? can return unexpected (at
> least to me) results in the sparse array case (see below)
> Is it because 'enum precreates key nodes in the tree for a sparse array in
> order to keep it balanced?.
>
>
Ok. I think I get the structure.
(val (enum? *HT Val) is the test I need to use and is very fast!

My apologies for the email noise as I worked through this.

/Lindsay

(de TestEnum (N)
   (off *HT)
   (for I N
  (let (Val (Hasher I))
 (if (val (enum? *HT Val))
(prinl
   "! Duplicate: "
   I
   "; "
   Val
   "; "
   (sym (enum? '*HT Val))
   "; "
   (sym (val (enum '*HT Val))) )


 (set (enum '*HT Val) I) ) ) ) )



: (TestEnum (** 2 16))
-> 65536
: (TestEnum (** 2 18))
-> 262144
: (TestEnum (** 2 20))
-> 1048576


(em)

2025-01-28 Thread Jean-Christophe Helary
Is it my (macOS) system or (em) does not work ?

I guess it must be a library that is not loaded at launch ?

pil -em +
!? (em)
em -- Undefined


-- 
Jean-Christophe Helary
https://sr.ht/~brandelune/
@jchelary@sciences.social



--
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


Connecting stderr

2025-01-28 Thread Lindsay Lawrence
Hi,

When calling, or piping, to an external process from picolisp, is there a
way to connect the stderr of that process on another channel as well, so
that it can be read, and distinguished, from stdout.


/Lindsay


Subscribe

2025-01-28 Thread Emon Sahariar

Hello Emon Sahariar  :-)
You are now subscribed




--
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


Re: How to set initial job environment values?

2025-01-28 Thread Lindsay Lawrence
>
>
>
> 'curry' is a bit overkill here. I would directly 'fill' a 'job':
>

Thanks! 'fill' is indeed the function I was looking for here.
Working with 'nth', I did learn more about the picolisp machine and list
internals.

/Lindsay

z


Re: Delayed Mails

2025-01-28 Thread Lindsay Lawrence
On Tue, Jan 28, 2025 at 9:47 AM Alexander Burger 
wrote:

> Sorry for the five delayed Mails! It was my fault :(
>
> ☺/ A!ex


👍


Re: How to set initial job environment values?

2025-01-28 Thread Alexander Burger
On Sun, Jan 26, 2025 at 12:51:10AM -0800, Lindsay Lawrence wrote:
> Is there a simpler way to set the curry environment's State value to Seed
> than what I am doing here?
> 
> Regards,
> Lindsay
> 
> (de randomLehmer (Seed)
>(let
>   (Fn
>  (curry
> ((State . 1))
> NIL
> (setq State (% (* State 279470273) 4294967291)) ) )
>   (set
>  (nth Fn 2 2 2)
>  (cons
> 'State
> (if Seed Seed (inc (abs (rand ) )
>   Fn ) )
> 
> 
> : (pretty (randomLehmer 97))
> (NIL
>(job '((State . 97))
>   (setq State (% (* State 279470273) 4294967291)) ) )-> ")"

'curry' is a bit overkill here. I would directly 'fill' a 'job':

   (de randomLehmer (Seed)
  (let @Seed (or Seed (inc (abs (rand
 (fill
'(()
   (job '((State . @Seed))
  (setq State (% (* State 279470273) 4294967291)) ) ) ) ) )

: (pretty (randomLehmer 97))
(NIL
   (job '((State . 97))
  (setq State (% (* State 279470273) 4294967291)) ) )-> ")"

☺/ A!ex

-- 
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


picolisp db encryption?

2025-01-28 Thread Lindsay Lawrence
Question: Is it possible to encrypt picolisp dbs on the fly? For example,
to hook into the low level functions that do block reads and writes in
order to apply encryption?

For example, it would be a useful option, when setting up a 'pool'  to
specify a key that is used to decrypt/encrypt block reads/writes to the
underlying storage. Once set up, this action would be transparent to the
application.

/Lindsay


Re: picolisp db encryption?

2025-01-28 Thread Alexander Burger
On Tue, Jan 28, 2025 at 11:10:25AM -0800, Lindsay Lawrence wrote:
> Question: Is it possible to encrypt picolisp dbs on the fly? For example,
> to hook into the low level functions that do block reads and writes in
> order to apply encryption?

Not so easily.

If it were only for reading from such a DB, it would be possible via
the '*Ext' mechanism. With that, almost anything can be read as a
database (for example PilBox is doing that to access the Java Objects in
the Android runtime).

But for writing I see no easy way at the moment.

> For example, it would be a useful option, when setting up a 'pool'  to
> specify a key that is used to decrypt/encrypt block reads/writes to the
> underlying storage. Once set up, this action would be transparent to the
> application.

Also, I'm not sure if this is really useful, as it could always be
locally intercepted.

Needs more thinking ...

☺/ A!ex


-- 
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


unsubscribe

2025-01-28 Thread Alex Williams

Good bye Alex Williams  :-(
You are now unsubscribed



--
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


Re: Connecting stderr

2025-01-28 Thread Lindsay Lawrence
On Sat, Jan 25, 2025 at 8:56 AM Lindsay Lawrence <
lawrence.lindsayj...@gmail.com> wrote:

> Is there a straightforward way from PicoLisp, when executing an external
> process, to get stderr on a separate channel?
> I'd like to do something like (pipe) where I can connect stdin and stdout,
> but have an additional channel for the child's stderr
>
> After a bit of hacking, I've come up with the code below:

It uses pipe, redirects stderr of the Exe to a file and returns a list with
the stdout lines in the car and stderr in the cadr

I'm pretty happy with it :)

/Lindsay

(de runExe (Cmds . Exe)
   (use (LOut LErr)
  (let (ErrFile (tmp "exerr." (rand)))
 (finally (call "rm" ErrFile)
(pipe
   (err ErrFile (out Exe (mapc prinl Cmds)))
   (setq LOut (make (while (line T) (link @ )
(in ErrFile
   (setq LErr (make (while (line T) (link @ ) ) )
  (list LOut LErr) ) )


which works something like   this (note the error in the last command sent)

: (mapc '((X) (mapc println X))
(runExe '(".mode quote"
  "select strftime('%s', 'now'), date('now');"
  "select date();"
  "select time();"
  "select datetim();"
  ".exit")
sqlite3 "file:mdb?mode=memory" ))
"'1737836168','2025-01-25'"
"'2025-01-25'"
"'20:16:08'"
"Error: near line 5: no such function: datetim"
-> "Error: near line 5: no such function: datetim"


Re: How to use enum? to check for existence in sparse array

2025-01-28 Thread Alexander Burger
On Mon, Jan 13, 2025 at 10:58:55PM -0800, Lindsay Lawrence wrote:
> Ok. I think I get the structure.
> (val (enum? *HT Val) is the test I need to use and is very fast!

Right.

Sorry once more for the late reply!


> My apologies for the email noise as I worked through this.

No problem! ;)

☺/ A!ex

-- 
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


Updated wiki example

2025-01-28 Thread Lindsay Lawrence
Hi,

https://picolisp.com/wiki/?RunExe

I optimized the code used in the example I previously added to the wiki. Being
able to pipe 1M inserts into a sqlite db in ~2.5secs from picolisp is all
kinds of awesome.

I also added a link to an additional example:
https://github.com/thinknlive/picolisp-lisp-basics/blob/master/sqlite.l

In the additional example, I have added a couple of functions to quote sql
parameters
and a (just) good enough parser for results returned as json, allowing
reliably correct handling of unicode and escaped characters on inserts and
queries. (see bench code below)

Happy Hacking
/Lindsay


: (bench (let (Sql '(
  ".mode quote"
  "drop table if exists test;"
  "create table test(id integer primary key, name text, cnt int default 1);"
  "create unique index uix_test_name on test(name);"
  ))
  (setq Sql
(append
  Sql
  '("begin transaction;")
  (make (do 1 (link (pack
"insert into test (name) values (" (sqlQuote (makeRndName 4)) ") on
conflict (name) do update set cnt = cnt + 1;"
  '("commit;")
  '("select json_array(id, name, cnt, (hex(randomblob(8 from test
where cnt > 1;")
  '("select json_array('CollisionCount', count(*)) from test where cnt
> 1;")
  '("select json_array('RecordCount', count(*)) from test;")))
  (bench (mapc '((R) (let (Row (head -2 (nth (chop R) 3))
   Flds (fldParseQ Row))
 (prin R " --> ") (print Flds) (prinl  " : " (length
Flds
(car (runExe Sql sqlite3 "testdb.sqlite"))) ]
'[31,"99\\,",2,"0A0427635B128F1B"]' --> ("31" "99\\," "2"
"0A0427635B128F1B") : 4
'[32,"6,👍3",2,"0A15EE4D50BA7DED"]' --> ("32" "6,👍3" "2"
"0A15EE4D50BA7DED") : 4
'[142,"👍238",2,"5FB33CBC8DC3B366"]' --> ("142" "👍238" "2"
"5FB33CBC8DC3B366") : 4
'[266,"CIᘐU",2,"4AB7193F10CEBB55"]' --> ("266" "CIᘐU" "2"
"4AB7193F10CEBB55") : 4
'[472,"\np👍\"",2,"979F6057BCD7C97B"]' --> ("472" "^Jp👍\"" "2"
"979F6057BCD7C97B") : 4
'[990,"\\n👍3",2,"5F3F9E2031EA3C52"]' --> ("990" "\\n👍3" "2"
"5F3F9E2031EA3C52") : 4
'[1546,"\n1,6",2,"0CB9CE64B32AFE6D"]' --> ("1546" "^J1,6" "2"
"0CB9CE64B32AFE6D") : 4
'[1836,",\\5ᘐ",2,"9FD2D32906D5081A"]' --> ("1836" ",\\5ᘐ" "2"
"9FD2D32906D5081A") : 4
'[2188,"8FZ👍",2,"C3D2C1661145BE91"]' --> ("2188" "8FZ👍" "2"
"C3D2C1661145BE91") : 4
'[2239,"\"9ᘐ\"",2,"C61BF491F245F996"]' --> ("2239" "\"9ᘐ\"" "2"
"C61BF491F245F996") : 4
'[3106,",\n2L",2,"C33A015041A908B0"]' --> ("3106" ",^J2L" "2"
"C33A015041A908B0") : 4
'[3731,"\\👍\"9",2,"AD7232A882E4BD03"]' --> ("3731" "\\👍\"9" "2"
"AD7232A882E4BD03") : 4
'[3748,"\n\",\\",2,"700011DF8C20EEED"]' --> ("3748" "^J\",\\" "2"
"700011DF8C20EEED") : 4
'[5288,"3g👍\\",2,"D5F23C683C48CA23"]' --> ("5288" "3g👍\\" "2"
"D5F23C683C48CA23") : 4
'[6579,"\"\"\n,",2,"8C594685D3E210CF"]' --> ("6579" "\"\"^J," "2"
"8C594685D3E210CF") : 4
'["CollisionCount",15]' --> ("CollisionCount" "15") : 2
'["RecordCount",9985]' --> ("RecordCount" "9985") : 2
0.135 sec
0.158 sec
-> 2
:


Re: Connecting stderr

2025-01-28 Thread Lindsay Lawrence
Hi,

Re-asking the question:
Is there a straightforward way from PicoLisp, when executing an external
process, to get stderr on a separate channel?
I'd like to do something like (pipe) where I can connect stdin and stdout,
but have an additional channel for the child's stderr

/Lindsay



On Wed, Jan 15, 2025 at 10:02 AM Lindsay Lawrence <
lawrence.lindsayj...@gmail.com> wrote:

> Hi,
>
> When calling, or piping, to an external process from picolisp, is there a
> way to connect the stderr of that process on another channel as well, so
> that it can be read, and distinguished, from stdout.
>
>
> /Lindsay
>


How to set initial job environment values?

2025-01-28 Thread Lindsay Lawrence
Hi,

Is there a simpler way to set the curry environment's State value to Seed
than what I am doing here?

Regards,
Lindsay

(de randomLehmer (Seed)
   (let
  (Fn
 (curry
((State . 1))
NIL
(setq State (% (* State 279470273) 4294967291)) ) )
  (set
 (nth Fn 2 2 2)
 (cons
'State
(if Seed Seed (inc (abs (rand ) )
  Fn ) )


: (pretty (randomLehmer 97))
(NIL
   (job '((State . 97))
  (setq State (% (* State 279470273) 4294967291)) ) )-> ")"
:

>
>


Re: (em)

2025-01-28 Thread Alexander Burger
On Tue, Jan 14, 2025 at 09:44:10AM +, Jean-Christophe Helary wrote:
> Is it my (macOS) system or (em) does not work ?
> 
> I guess it must be a library that is not loaded at launch ?
> 
> pil -em +
> !? (em)
> em -- Undefined

AFAIK (em) was supported only on pil64, not on current pil21.

Not sure what would be needed to port it.

☺/ A!ex

-- 
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


Delayed Mails

2025-01-28 Thread Alexander Burger
Sorry for the five delayed Mails! It was my fault :(

☺/ A!ex

-- 
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe


Re: Subscribe

2025-01-28 Thread Jean-Christophe Helary



> On Jan 13, 2025, at 20:21, Alexander Burger  wrote:
> 
> On Sun, Jan 12, 2025 at 02:53:49AM +, Jean-Christophe Helary wrote:
>> Hello Jean-Christophe Helary  
>> :-)
>> You are now subscribed
>> 
>> 
>> Am I subscribed ?
> 
> Yes :) Though this answer is probably not helpful, because you either
> don't see it, or you received your own posts already.

:)

> I have no info about the Mac issues.

I don’t know about llvm-16 being required, it is just a message I got from the 
make process that hinted at that.

As for the aliases, it looks like something is fishy, but I am not sure yet if 
it’s my system of the way the distribution does (not) inform users about the 
instructions required to create aliases.

When this is sorted out, I’ll update the wiki.

-- 
Jean-Christophe Helary
https://sr.ht/~brandelune/
@jchelary@sciences.social



--
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe