@@ 1,46 @@
+local attrdb = {}
+
+local lpeg = require "lpeg"
+
+local P, B, R, S, V = lpeg.P, lpeg.B, lpeg.R, lpeg.S, lpeg.V
+local C, Carg, Cb, Cc, Cf, Cg, Cp, Cs, Ct, Cmt = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cp, lpeg.Cs, lpeg.Ct, lpeg.Cmt
+
+local NL = P '\n'
+local Space = S ' \t'
+local Quote = S [['"]]
+local Special = Space + Quote + NL + '='
+
+local Openquote = Cg(Quote, "quote")
+local Escquote = Cmt(
+ C(Quote) * C(Quote) * Cb("quote"),
+ function(s, pos, q1, q2, oq)
+ if q1 == oq and q2 == oq then
+ return true, oq
+ end
+ end
+)
+local Closequote = Cmt(
+ C(Quote) * Cb("quote"),
+ function(s, pos, q1, oq)
+ return q1 == oq
+ end
+)
+local Word = (1 - Special)^1
+local Quoted = Cs(((P(1) - Closequote) + Escquote)^0)
+local String = Cg(Word)
+ + Openquote * Cg(Quoted) * Closequote
+String = String / 1
+
+local Pair = Ct(String * '=' * String^-1)
+
+local Line = Ct(Pair * (Space^1 * Pair)^0)
+local Entry = Ct(Line * (NL * Space^1 * Line)^0)
+local Dbspace = NL + Space
+local Db = Ct(Dbspace^0 * Entry * (Dbspace^1 * Entry)^0)
+
+attrdb.String = String
+attrdb.Pair = Pair
+attrdb.Entry = Entry
+attrdb.Db = Db
+
+return attrdb
@@ 1,94 @@
+#!/bin/lu9
+
+insp = require "inspect"
+local attrdb = require "attrdb"
+
+local teststring = {
+ [[unquoted]],
+
+ [[""]],
+ [["string"]],
+ [["string with spaces"]],
+
+ [['single quote ('')']],
+ [["double quote ("")"]],
+
+ [['outer single quote ('') (")']],
+ [["outer double quote (') ("")"]],
+
+ [["multiline
+strings
+work"]],
+
+ [['("'') mix']],
+}
+
+local testpair = {
+ [[novalue=]],
+ [["long key no value"=]],
+ [[key=value]],
+ [["long key"="long value"]],
+ [["multiline
+ key"="multiline
+ value"]],
+
+ [[=nokey]],
+ [[=]],
+}
+
+local testentry = {
+ [[key=value k=v]],
+[[
+key=value
+ newline=nl
+]],
+[[
+k1=v1 "k2"="v2"
+ newline=nl
+ another="newline is here"
+nextentry=
+]],
+[[
+k1=v1
+ continuation=
+
+ even=this
+]],
+[[
+k=v k2=v2
+ cont=space
+ cont=tab
+ many=tabs
+next=entry
+]],
+[[
+k=v
+next=entry
+]]
+}
+
+local testdb = {
+[[
+
+nick=a
+ mail=a@example.com
+ mail=bla@example.com preffered=
+ www=http://a.example.com
+ git=https://git.example.com/a
+
+nick=b
+ mail=b@example.com
+ www=http://b.example.com
+nick=c nick=C
+ mail=c@example.com
+ www=http://c.example.com www=http://cccc.example.com
+ git=https://git.example.com/c
+
+
+]]
+}
+
+for _, buf in ipairs(testdb) do
+ local m = attrdb.Db:match(buf)
+ io.write(string.format("%s\n%s\n", ln, insp(m)))
+end