]> git.plutz.net Git - lobster/commitdiff
merge from confetti
authorPaul Hänsch <paul@plutz.net>
Sat, 6 Feb 2021 22:40:32 +0000 (23:40 +0100)
committerPaul Hänsch <paul@plutz.net>
Sat, 6 Feb 2021 22:40:32 +0000 (23:40 +0100)
1  2 
cards/edit_card.sh
cards/index.cgi
cards/l10n.sh
cards/list.sh
cards/update_card.sh
cards/widgets.sh
index.cgi
pdiread.sh

diff --combined cards/edit_card.sh
index a29a098ad76f3fe735c54cc5ab2d54c4e5444e62,4239cc5968c03b5e58d11a96d0e0ae22f68de41f..16d759999893c539a5884390ccc147a68772192b
@@@ -1,4 -1,4 +1,4 @@@
- #!/bin/zsh
+ #!/bin/sh
  
  # Copyright 2019 Paul Hänsch
  #
@@@ -26,11 -26,11 +26,11 @@@ filter="$(REF f)
  order="$(REF o)"
  
  if tempfile="$(SLOCK "$cardfile" "$locktimeout")"; then
 -  REDIRECT "/cards/?o=${order}&f=${filter}&e=${card}"
 +  REDIRECT "/cards/?o=${order}&f=${filter}&e=${card#/}"
  elif [ -f "$tempfile" ]; then
    SET_COOKIE session message="SESSLOCK"
 -  REDIRECT "/cards/?o=${order}&f=${filter}#${card}"
 +  REDIRECT "/cards/?o=${order}&f=${filter}#${card#/}"
  else
    SET_COOKIE session message="EDITLOCK"
 -  REDIRECT "/cards/?o=${order}&f=${filter}#${card}"
 +  REDIRECT "/cards/?o=${order}&f=${filter}#${card#/}"
  fi
diff --combined cards/index.cgi
index 43f949c3a7fd256bec436c61b46988970c6958e2,934c19ac3bf2499b3aee9adf0440188739a6e3f2..0bb4f827c26cf53b7c934039d5260f3a88abe739
@@@ -1,38 -1,25 +1,30 @@@
  #!/bin/sh
  
- if [ "$_PATH" = "/cards/cards.css" ]; then
-   . $_EXEC/cgilite/file.sh
-   FILE $_EXEC/cards/cards.css
-   return 0
- fi
  . $_EXEC/pdiread.sh
  . $_EXEC/cards/l10n.sh
  . $_EXEC/cards/widgets.sh
  . $_EXEC/cards/list.sh
  
- #unescape() { [ $# = 0 ] && sed -E 's;\\(.);\1;g' || printf %s "$*" |sed -E 's;\\(.);\1;g'; }
  upcase=' y;abcdefghijklmnopqrstuvwxyzäöüé;ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉ;; '
  
  filter="$(GET f)"
  order="$(GET o)"
  edit="$(GET e |PATH)"
  
 -[ "$order" ] || order=firstname
 +[ "$order" ] || order=lastname
  edit="${edit##*/}"
  
 +list_hi_companies(){
 +  sed -rn 's;^X-HEALTH-INSURANCE:([^\;]+)\;.*$;\1;p' ${_DATA}/vcard/*vcf \
 +  | sort -u
 +}
 +
  { w_filter_diag
    printf '
    [form class="newcard" action="/cards/new_card.sh" method="POST"
 +    [a href="#top" . %s]
      [button type="submit" %s]
 -    [input name="seed" placeholder="%s"]
 -  ]' "$(l10n newcard)" "$(l10n vcf_seed_label)"
 +  ]' "$(l10n top)" "$(l10n newcard)"
    [ "$edit" ] && edit_card "$edit"
    list_cards
- } | yield_page cards /cards/cards.css
+ } | yield_page cards #/cards/cards.css
diff --combined cards/l10n.sh
index f10007e92212e92286cad4992780043acc45355a,2d9dc06362d250a5719f378aa0e9ef8a61867bee..f991e4fddb901c95fa7ed02039c691075a5e6632
@@@ -1,4 -1,4 +1,4 @@@
- # Copyright 2014, 2016, 2019 Paul Hänsch
+ # Copyright 2014, 2016, 2019, 2021 Paul Hänsch
  #
  # This file is part of Confetti.
  # 
  
  l10n(){
    local word
-   [ $# -eq 0 ] && read -r word || word="$1"
+   [ $# -eq 0 ] && read -r word || word="$*"
    case $word in
-     PHOTO) printf %s "Foto";;
-     LOGO) printf %s "Logo";;
-     FN) printf %s "Voller Name";;
-     N) printf %s "Name";;
-     n_pre) printf %s "Titel";;
-     n_first) printf %s "Vorname";;
-     n_middle) printf %s "Mittelnamen";;
-     n_last) printf %s "Nachname";;
-     n_post) printf %s "Zusätze";;
-     NICKNAME) printf %s "Spitzname";;
-     SOUND) printf %s "Aussprache";;
-     GENDER) printf %s "Geschlecht";;
-     KIND) printf %s "Typ";;
-     TITLE) printf %s "Beruf";;
-     ROLE) printf %s "Position";;
-     ORG) printf %s "Organisation";;
-     MEMBER) printf %s "Mitglied";;
-     CATEGORIES) printf %s "Kategorien";;
-     ANNIVERSARY) printf %s "Jubiläum";;
-     BDAY) printf %s "Geburtstag";;
-     EMAIL) printf %s "E-Mail";;
-     TEL) printf %s "Telefon";;
-     teltype) printf %s "Anschlusstyp:";;
-     TYPE=HOME) printf %s "Privat";;
-     TYPE=WORK) printf %s "Geschäftlich";;
-     TYPE=CELL) printf %s "Mobil";;
-     TYPE=FAX) printf %s "Fax";;
-     IMPP) printf %s "Chat";;
-     ADR) printf %s "Anschrift";;
-     URL) printf %s "Webseite";;
-     LANG) printf %s "Sprache";;
-     NOTE) printf %s "Notiz";;
-   
-     RELATED) printf %s "Kontakte";;
-   
-     BEGIN) printf %s "";;
-     CALADRURI) printf %s "";;
-     CALURI) printf %s "";;
-     CLASS) printf %s "";;
-     CLIENTPIDMAP) printf %s "";;
-     END) printf %s "";;
-     FBURL) printf %s "";;
-     GEO) printf %s "";;
-     MAILER) printf %s "";;
-     NAME) printf %s "";;
-     PRODID) printf %s "";;
-     PROFILE) printf %s "";;
-     REV) printf %s "";;
-     SORT-STRING) printf %s "";;
-     SOURCE) printf %s "";;
-     TZ) printf %s "";;
-     UID) printf %s "";;
-     VERSION) printf %s "";;
-     XML) printf %s "";;
-   
+     newcard) printf %s "Neuen Eintrag anlegen";;
      X-HEALTH-INSURANCE) printf %s "Kran&shy;ken&shy;ver&shy;sich&shy;er&shy;ung";;
      hi_from_list) printf %s "Aus Liste";;
      hi_other) printf %s "Andere";;
      hi_status) printf %s "Ver&shy;sich&shy;er&shy;ten&shy;sta&shy;tus";;
      X-HEALTH-INSURANCE-NOCONTRIB) printf %s "Zu&shy;zahl&shy;ungs&shy;be&shy;frei&shy;ung";;
      X-CLIENT-REFERRAL) printf %s "Empfehl&shy;ung durch";;
-     prescriptions) printf %s "Verordnungen";;
-     new_prescription) printf %s "Neue Verordnung";;
+     prescriptions) printf %s "Verord&shy;nungen";;
+     new_prescription) printf %s "Neue Verord&shy;nung";;
      no_icd) printf %s "Kein ICD Code";;
 +    therapy) printf %s "Therapie";;
 +    therapies) printf %s "Therapien";;
    
-     X-ZACK-JOINDATE)  printf %s "An&shy;mel&shy;de&shy;da&shy;tum";;
-     X-ZACK-LEAVEDATE) printf %s "Ab&shy;mel&shy;de&shy;da&shy;tum";;
+     X-ZACK-JOINDATE)  printf %s "Anmelde&shy;datum";;
+     X-ZACK-LEAVEDATE) printf %s "Abmelde&shy;datum";;
      X-ZACK-JOINDATE_short)  printf %s "Anm.";;
      X-ZACK-LEAVEDATE_short) printf %s "Abm.";;
-   
-     top) printf 'Seitenanfang';;
-     edit) printf %s "Bearbeiten";;
-     edit_categories) printf %s "Kategorien Bearbeiten";;
-     vcf_export) printf %s "Vcard Exportieren";;
-     control) printf %s "Aktionen";;
-     edit_update) printf %s "Daten übernehmen";;
-     edit_cancel) printf %s "Abbrechen";;
-     edit_delete) printf %s "Eintrag löschen";;
-     edit_addfieldtext) printf %s "Neues Feld";;
-     edit_addfield) printf %s "+";;
-     edit_deletefield) printf %s "X";;
-     filter_label) printf %s "Filter";;
-     filter_placeholder) printf %s "Begriffe zur Eingrenzung eingeben";;
-     filter_type) printf %s "Filtertyp";;
-     filter_order) printf %s "Sortierung";;
-     filter_any) printf %s "Alles";;
-     filter_name) printf %s "Name";;
-     filter_firstname) printf %s "Vorname";;
-     filter_lastname) printf %s "Nachname";;
-     filter_street) printf %s "Straße";;
-     filter_zip) printf %s "PLZ.";;
-     filter_TEL) printf %s "Telefon";;
-     filter_BDAY) printf %s "Geburtsjahr";;
-     filter_bdate) printf %s "Geburtsdatum";;
-     filter_course) printf %s "Kurs";;
-     filter_CATEGORIES) printf %s "Kategorien";;
-     filter_apply) printf %s "Filtern";;
-     filter_cancel) printf %s "Filter löschen";;
-     newcard) printf %s "Neuen Eintrag anlegen";;
-     course_attendance) printf %s "Kursteilnahme";;
-   
-     gender_none) printf %s "keine Angabe";;
-     gender_female) printf %s "Weiblich";;
-     gender_male) printf %s "Männlich";;
-     gender_other) printf %s "Sonstiges";;
-   
-     female) printf %s "&#x2640;";;
-     male) printf %s "&#x2642;";;
-     other) printf %s "&#x26A5;";;
-     none) printf %s "&#x26AA;";;
  
-     *) printf %s "$1";;
+     *) l10n_global "$word";;
    esac
  }
  
+ # BEGIN) printf %s "";;
+ # CALADRURI) printf %s "";;
+ # CALURI) printf %s "";;
+ # CLASS) printf %s "";;
+ # CLIENTPIDMAP) printf %s "";;
+ # END) printf %s "";;
+ # FBURL) printf %s "";;
+ # GEO) printf %s "";;
+ # MAILER) printf %s "";;
+ # NAME) printf %s "";;
+ # PRODID) printf %s "";;
+ # PROFILE) printf %s "";;
+ # REV) printf %s "";;
+ # SORT-STRING) printf %s "";;
+ # SOURCE) printf %s "";;
+ # TZ) printf %s "";;
+ # UID) printf %s "";;
+ # VERSION) printf %s "";;
+ # XML) printf %s "";;
diff --combined cards/list.sh
index 9d5457ce2bdd5429259f34eaba4f78684f0e10bf,cd0d21f1e320aa958839b8fdbe75dec051286da9..03fa97340bd546d83cdccc36f35b9c2ed90401ab
@@@ -13,36 -13,62 +13,41 @@@ edit_card()
    else
      card="$(pdi_load "$tempfile")"
      cat <<-EOF
 -      [form .card #${cardfile##*/} action="/cards/update_card.sh" method="POST"
 +      [span .card-anchor #${cardfile##*/}]
 +      [form .card action="/cards/update_card.sh" method="POST"
          [input type="hidden" name="tid" value="$(transid ${tempfile})"]
          [div .section .basic $(
            edit_item "$card" N GENDER
            [ "$(pdi_count "$card" NICKNAME)" -gt 0 ] \
            && edit_item "$card" NICKNAME
            edit_item "$card" BDAY
 -          edit_item "$card" X-ZACK-JOINDATE
 -          [ "$(pdi_count "$card" X-ZACK-LEAVEDATE)" -gt 0 ] \
 -          && edit_item "$card" X-ZACK-LEAVEDATE
            card_item "$card" SOUND PHOTO LOGO
          )]
 -        [div .section .phone   $(edit_item "$card" TEL)]
 -        [div .section .message $(
 -          edit_item "$card" EMAIL
 -          [ $(pdi_count "$card" IMPP) -gt 0 ] && edit_item "$card" IMPP
 -          [ $(pdi_count "$card" URL ) -gt 0 ] && edit_item "$card" URL
 -        )]
 +        [div .section .phone   $(
 +            edit_item "$card" TEL EMAIL
 +          [ $(pdi_count "$card" IMPP) -gt 0 ] \
 +            && edit_item "$card" IMPP
 +          [ $(pdi_count "$card" URL ) -gt 0 ] \
 +            && edit_item "$card" URL
 +          )]
          [div .section .address $(edit_item "$card" ADR)]
 -        [div .section .note    $(edit_item "$card" NOTE)]
 -        [div .section .attendance
 -          [h3 $(l10n course_attendance) ] $(
 -          for course in "$_DATA"/ical/*.ics; do
 -            printf '[label [input type="checkbox" name="attendance" value="%s" %s] %s]' \
 -                   "${course##*/}" \
 -                   "$(grep -qF "${course##*/} ${cardfile##*/}" "$_DATA/mappings/attendance" \
 -                      && printf 'checked="checked"'
 -                     )" \
 -                   "$(pdi_value "$(pdi_load "$course")" SUMMARY || l10n "(unnamed course)" |unescape |HTML)"
 -          done)
 -          [h3 $(l10n CATEGORIES) ] $(
 -          grep -xE '[^ ]+' "$_DATA"/mappings/categories |while read -r cat; do
 -            printf '[label [input type="checkbox" name="CATEGORIES" value="%s" %s] %s]' \
 -                   "$(HTML "$cat")" \
 -                   "$(seq 1 $(pdi_count "$card" CATEGORIES) |while read c; do
 -                     pdi_value "$card" CATEGORIES $c |grep -qxF "$cat" \
 -                     && printf 'checked="checked"' && break
 -                   done)" \
 -                   "$(HTML "$cat")"
 -          done)
 -        ]
 +        [div .section .insurance $(edit_item "$card" X-HEALTH-INSURANCE)]
 +        [div .section .note    $(edit_item "$card" NOTE X-CLIENT-REFERRAL)]
          [div .control
-           [select .item name="newfield"
-             [option value="" disabled="disabled" selected="selected" $(l10n edit_addfieldtext)]
-             $(for f in NICKNAME EMAIL TEL IMPP ADR URL NOTE; do
-               printf '[option value="%s" %s] ' "$f" "$(l10n "$f")"
-             done)
-          ][button .item type="submit" name="action" value="addfield" $(l10n edit_addfield)]
+           [div .item .delete label="$(l10n edit_delete)"
+               [input type="checkbox" #delete]
+               [label for="delete" $(l10n edit_delete)]
+             [button type="submit" name="action" value="delete" $(l10n edit_delete)]
+             ]
+             [div .item .newfield
+               [select name="newfield"
+               [option value="" disabled="disabled" selected="selected" $(l10n edit_addfieldtext)]
+               $(for f in NICKNAME EMAIL TEL IMPP ADR URL NOTE; do
+                 printf '[option value="%s" %s] ' "$f" "$(l10n "$f")"
+               done)
+             ][button type="submit" name="action" value="addfield" $(l10n edit_addfield)]
+             ]
            [button .item type="submit" name="action" value="update"   $(l10n edit_update)]
-           [input type="checkbox" #delete] [label .item for="delete" $(l10n edit_delete)]
-             [button .item type="submit" name="action" value="delete"]
            [button .item type="submit" name="action" value="cancel"   $(l10n edit_cancel)]
          ]
          [input type="hidden" name="UID" value="$(pdi_value "$card" UID |HTML)"]
  print_card(){
    local cardfile="$1"
    local card="$(pdi_load "$cardfile")"
 +  local N1 N2 N3 N4 N5
 +  IFS=\; read N1 N2 N3 N4 N5 <<-EOF
 +      $(pdi_value "$card" N |pdi_unescape |HTML)
 +      EOF
 +
    cat <<-EOF
-     [span .card-anchor #${cardfile##*/}]
-     [div .card
+     [div .card #${cardfile##*/}
 -      [div .section .basic . $(
 -        card_item "$card" FN GENDER NICKNAME BDAY X-ZACK-JOINDATE X-ZACK-LEAVEDATE SOUND PHOTO LOGO
 +      [div .section .basic
 +      [h2 .item .FN . $N4 $N1 $N5]
 +      [span .item .firstname . $N2 $N3]
 +      $(
 +        card_item "$card" GENDER NICKNAME BDAY X-ZACK-JOINDATE X-ZACK-LEAVEDATE SOUND PHOTO LOGO
        )]
 -      [div .section .phone   . $(card_item "$card" TEL)]
 -      [div .section .message . $(card_item "$card" EMAIL IMPP URL)]
 -      [div .section .address . $(card_item "$card" ADR)]
 -      [div .section .note    . $(card_item "$card" NOTE)]
 -      [div .section .attendance [h3 $(l10n course_attendance) ] [ul
 -        $(grep -F "   ${cardfile##*/}" "$_DATA/mappings/attendance" |while read each discard; do
 -          printf '[li [a .item .attendance href="/courses#%s" . %s]]' \
 -                 "$each" \
 -                 "$(pdi_value "$(pdi_load "$_DATA/ical/$each")" SUMMARY || l10n "(unnamed course)" |unescape |HTML)"
 -        done)]
 -        $(card_item "$card" CATEGORIES)
 -      ]
 +      [div .section .address   $(card_item "$card" ADR)]
 +      [div .section .phone     $(card_item "$card" TEL EMAL IMPP URL)]
 +      [div .section .insurance $(card_item "$card" X-HEALTH-INSURANCE)]
 +      [div .section .note      $(card_item "$card" NOTE X-CLIENT-REFERRAL)]
 +      [div .section .therapies $(card_item "$card" therapies)]
        [div .control
          [a .item href="/cards/edit_card.sh?card=${cardfile##*/}" $(l10n edit)]
          [a .item href="/cards/export_card.sh?card=${cardfile##*/}" $(l10n vcf_export)]
@@@ -87,6 -111,8 +91,6 @@@ print_cards()
  
    while read cardfile; do
      cachefile="${_DATA}/cache/${cardfile##*/}.cache"
 -    # if [ -s "$cachefile" -a "$cachefile" -nt "$cardfile" \
 -    #                      -a "$cachefile" -nt "${_EXEC}/cards" ]; then
      if [ -s "$cachefile" -a "$cachefile" -nt "$cardfile" ]; then
        cat "$cachefile"
      else
    done
  }
  
 -filter_attendance(){
 -  fatt="$1"
 -  attfile="$_DATA/mappings/attendance"
 -
 -  if [ ! "$fatt" ]; then
 -    # debug 'list all'
 -    printf '%s\n' "$_DATA/vcard"/*.vcf
 -  elif [ "${fatt#* }" = "${fatt}" ]; then
 -    # debug "list $fatt"
 -    grep -xiE "(${fatt})      .+vcf" "$attfile" \
 -    | while read vcf; do
 -      printf '%s/vcard/%s\n' "$_DATA" "${vcf##*       }"
 -    done
 -  else
 -    # debug "filter ${fatt%% *}"
 -    filter_attendance "${fatt#* }" \
 -    | while read vcf; do
 -      grep -xiE "(${fatt%% *})        ${vcf##*/}" "$attfile"
 -    done \
 -    | while read vcf; do
 -      printf '%s/vcard/%s\n' "$_DATA" "${vcf##*       }"
 -    done
 -  fi
 -}
 -
  filter_cards(){
    local filter f fex='x;p;'
  
    filter="$(printf %s "${filter}" \
-             | sed -E 's;[]\/\(\)\\\$\?\.\+\*\;\[\{\}];\\\\&;g;
+             | sed -E 's;[]\/\(\)\\\$\?\.\+\*\;\[\{\}];\\&;g;
                        '"$upcase"
             )^"
  
    while [ "$filter" ]; do
      f="${filter%%^*}" filter="${filter#*^}"
 +    debug "Filter: $f"
      case $f in
        '') break
          ;;
-       ANY:*) fex="/\n.*(\;[^:]*)?:.*(${f#*:}).*\r?\n/{${fex}}"
 -      COURSE:*) fatt="${fatt}${fatt:+ }${f#*:}"
 -        ;;
+       ANY:*) fex="/\n.*(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
          ;;
-       NAME:*) fex="/\n(N|FN|NICKNAME)(\;[^:]*)?:[^\n]*(${f#*:}).*\r?\n/{${fex}}"
+       NAME:*) fex="/\n(N|FN|NICKNAME)(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
          ;;
-       STREET:*|ZIP:*) fex="/\nADR(\;[^:]*)?:[^\n]*(${f#*:}).*\r?\n/{${fex}}"
+       STREET:*|ZIP:*) fex="/\nADR(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
          ;;
-       *) fex="/\n${f%%:*}(\;[^:]*)?:[^\n]*(${f#*:}).*\r?\n/{${fex}}"
+       *) fex="/\n${f%%:*}(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
          ;;
      esac
    done
  
 -  # for cardfile in "${_DATA}"/vcard/*.vcf; do
 -  filter_attendance "$fatt" |while read cardfile; do
 -    printf '%s\n' "$cardfile"
 -    cat "$cardfile"
 +  for cardfile in "${_DATA}"/vcard/*.vcf; do
 +    printf '%s\n' "$cardfile" "$(grep -vxE " *${CR}?" "$cardfile")"
    done \
 -  | sed -nE ':X; /\nEND\;?:VCARD\r?$/!{ N; bX; }; h; s;\n.*$;;; x; s;^[^\n]+\n;;;
 +  | sed -En ':X; /\nEND;?:VCARD\r?$/!{ N; bX; }; h; s;\n.*$;;; x; s;^[^\n]+\n;;;
               '"$upcase""$fex"
  }
  
diff --combined cards/update_card.sh
index 84d9c7e9b310662d403a9eb495dd8248a5796f54,d942e9abdbc527aa5ff803bc8c0b4dd501355778..2ee69318c28da5c502d6e89a4c9b6364cebbe9c3
@@@ -1,6 -1,6 +1,6 @@@
- #!/bin/zsh
+ #!/bin/sh
  
- # Copyright 2014, 2016, 2019 Paul Hänsch
+ # Copyright 2014, 2016, 2019, 2020, 2021 Paul Hänsch
  #
  # This file is part of Confetti.
  # 
  
  . "$_EXEC/pdiread.sh"
  . "$_EXEC/session_lock.sh"
+ . "$_EXEC/cgilite/storage.sh"
  
  unset filter order card action newfield
--unset cardfile attfile tempfile
++unset cardfile tempfile
  unset vcf field cnt delete_key
  
  filter="$(REF f)"
  order="$(REF o)"
  
- card="$(POST card |PATH)"
- cardfile="$_DATA/vcard/${card##*/}"
- attfile="$_DATA/mappings/attendance"
+ card="$(POST card |PATH)"; card="${card##*/}"
+ cardfile="$_DATA/vcard/${card}"
 -attfile="$_DATA/mappings/attendance"
  
  action="$(POST action)"
  newfield="$(POST newfield |grep -m 1 -xE '[A-Z][A-Z0-9-]*')"
@@@ -49,43 -50,40 +49,46 @@@ elif [ "$(POST tid)" != "$(transid "$te
    exit 0
  fi
  
 -vcf_escape(){
 -  for each in "$@"; do
 -    printf %s\\n "$each" \
 -    | sed -E ':X;$!{N;bX}; s;\r\n;\n;g; s;([;,\\]);\\\1;g; s;\n;\\n;g;'
 -  done \
 -  | sed -E ':X;$!{N;bX}; s;\n;\;;g'
 -}
 -
  # [ "${_POST[hi_select]}" = "list" ] || _POST[hi_company]="${_POST[hi_other]}"
  # [ -n "${_POST[hi_company]}${_POST[hi_number]}${_POST[hi_status]}" ] \
 -# && _POST[X-HEALTH-INSURANCE]="$(vcf_escape "${_POST[hi_company]}" "${_POST[hi_number]}" "${_POST[hi_status]}")"
 +# && _POST[X-HEALTH-INSURANCE]="$(pdi_escape "${_POST[hi_company]}" "${_POST[hi_number]}" "${_POST[hi_status]}")"
  
- # vcf="$(pdi_load "$cardfile")"
  vcf="$(pdi_load "$tempfile")"
  
- vcf="$(pdi_update_value "$vcf" N 1 "$(pdi_escape "$(POST 1N)" "$(POST 2N)" "$(POST 3N)" "$(POST 4N)" "$(POST 5N)")")"
- vcf="$(pdi_update_value "$vcf" FN 1 "$(pdi_escape "$(POST 4N) $(POST 2N) $(POST 3N) $(POST 1N) $(POST 5N)" \
-                                        | sed -E 's;^ +;;; s; +$;;; s; +; ;g;')" )"
+ n1="$(POST 1N)" n2="$(POST 2N)" n3="$(POST 3N)" n4="$(POST 4N)" n5="$(POST 5N)"
+ # 3N (Middle Names) is not actually used
+ n3="${n2#${n2%% *}}"
+ vcf="$(pdi_update_value "$vcf"  N 1 "$(vcf_escape "$n1" "${n2%% *}" "${n3# }" "$n4" "$n5")")"
+ vcf="$(pdi_update_value "$vcf" FN 1 "$(vcf_escape "$n4 $n2 $n1 $n5" |sed -E 's;(^ +| +$);;g; s; +; ;g;')")"
+ vcf="$(printf '%s\n' "$vcf" |sed -E "/^CATEGORIES;[^:]*:.*$/d")"
  
  for field in $(POST_KEYS |grep -xE '[A-Z][A-Z0-9-]*'); do
    for cnt in $(seq 1 $(POST_COUNT "$field")); do
      case "$field" in
        # (TEL)
 -      #   printf '%s;TYPE=%s:%s\r\n' "${field}" "${_POST[phonetype${key#TEL}]}" "$(vcf_escape "$(POST "$field" "$cnt")")"
 +      #   printf '%s;TYPE=%s:%s\r\n' "${field}" "${_POST[phonetype${key#TEL}]}" "$(pdi_escape "$(POST "$field" "$cnt")")"
        #   ;;
 +      X-HEALTH-INSURANCE)
 +        hi_select="$(POST "$field" "$cnt")"
 +        if [ "$hi_select" = list ]; then
 +          vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(pdi_escape "$(POST "hi_company" "$cnt")" \
 +                                                                       "$(POST "hi_number" "$cnt")" \
 +                                                                       "$(POST "hi_status" "$cnt")" \
 +                                                          )")"
 +        elif [ "$hi_select" = other ]; then
 +          vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(pdi_escape "$(POST "hi_other" "$cnt")" \
 +                                                                       "$(POST "hi_number" "$cnt")" \
 +                                                                       "$(POST "hi_status" "$cnt")" \
 +                                                          )")"
 +        fi
 +        ;;
        TEL)
           vcf="$(pdi_update_attrib "$vcf" TEL $cnt TYPE="$(POST teltype $cnt |grep -Exm1 'HOME|WORK|CELL|FAX')")"
 -         vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(vcf_escape "$(POST "$field" "$cnt")")")"
 +         vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(pdi_escape "$(POST "$field" "$cnt")")")"
           ;;
        *)
 -         vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(vcf_escape "$(POST "$field" "$cnt")")")"
 +         vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(pdi_escape "$(POST "$field" "$cnt")")")"
          ;;
      esac
  done; done
@@@ -103,41 -101,51 +106,26 @@@ vcf="$(printf '%s\n' "$vcf" |sed -E "/^
  if [ "$action" = addfield ]; then
    vcf="$(pdi_update_value "$vcf" "$newfield" $(( $(pdi_count "$vcf" "$newfield") + 1 )) '')"
  fi
 -printf '%s' "$vcf" |grep -vx '' >"$tempfile"
 +printf '%s' "$vcf" | sed -E '/^$/d; s/^([^:]+);:/\1:/;' >"$tempfile"
  
  case "$action" in
    addfield)
 -    REDIRECT "/cards/?o=${order}&f=${filter}&e=${card}"
 +    REDIRECT "/cards/?o=${order}&f=${filter}&e=${card#/}"
      ;;
    update)
-     # attendance=()
-     # for att in attendance attendance{0..100}; do
-     #   [ -n "${_POST[$att]}" ] && attendance+=("${_POST[$att]}")
-     # done
-     # sed -rn 's:^(.+)'$card'$:\1:p' "$attfile" |while read course; do
-     #   touch "$_DATA/ical/$course"
-     # done
-     # sed -i -r '/^(.+)\t'$card'$/d' "$attfile"
-     # for each in $attendance; do
-     #   echo "$each\t$card"
-     # done >>"$attfile"
-     # sed -rn 's:^(.+)'$card'$:\1:p' "$attfile" |while read course; do
-     #   touch "$_DATA/ical/$course"
-     # done
 -    if LOCK "$attfile"; then
 -      grep -F "       ${card}" "$attfile" |while read course junk; do
 -        touch "$_DATA/ical/${course}"
 -      done
 -      sed -i -E "/^.+ ${card}\$/d" "$attfile"
 -      seq 1 $(POST_COUNT attendance) |while read n; do
 -        printf '%s    %s\n' "$(POST attendance $n)" "$card"
 -      done >>"$attfile"
 -      grep -F "       ${card}" "$attfile" |while read course junk; do
 -        touch "$_DATA/ical/${course}"
 -      done
 -      RELEASE "$attfile"
 -    else
 -      SET_COOKIE 0 message="COULD NOT UPDATE COURSE MAPPINGS"
 -    fi
--
      cp "$tempfile" "$cardfile"
      RELEASE_SLOCK "$cardfile"
 -    REDIRECT "/cards/?o=${order}&f=${filter}#${card}"
 +    REDIRECT "/cards/?o=${order}&f=${filter}#${card#/}"
      ;;
    cancel)
      RELEASE_SLOCK "$cardfile"
      [ -f "$cardfile" ] \
 -    && REDIRECT "/cards/?o=${order}&f=${filter}#${card}" \
 +    && REDIRECT "/cards/?o=${order}&f=${filter}#${card#/}" \
      || REDIRECT "/cards/?o=${order}&f=${filter}"
      ;;
    delete)
      rm "$cardfile"
      RELEASE_SLOCK "$cardfile"
 -    if LOCK "$attfile"; then
 -      grep -F "       ${card}" "$attfile" |while read course junk; do
 -        touch "$_DATA/ical/${course}"
 -      done
 -      sed -i -E "/^.+ ${card}\$/d" "$attfile"
 -      RELEASE "$attfile"
 -    else
 -      SET_COOKIE 0 message="COULD NOT UPDATE COURSE MAPPINGS"
 -    fi
      REDIRECT "/cards/?o=${order}&f=${filter}"
      ;;
  esac
diff --combined cards/widgets.sh
index e1f659659ccec70221cad9fdc40cc9ba26f3ed26,c0c05946b1c594272d034a33081e351444873df9..9a71f198c4a9322ef6f465dba321290b5d6cc0f1
@@@ -1,4 -1,4 +1,4 @@@
- # Copyright 2014 - 2019 Paul Hänsch
+ # Copyright 2014 - 2019, 2021 Paul Hänsch
  #
  # This file is part of Confetti.
  # 
  # You should have received a copy of the GNU Affero General Public License
  # along with Confetti.  If not, see <http://www.gnu.org/licenses/>. 
  
 -list_categories() {
 -  grep -vxE '^[       ]*$' "${_DATA}/mappings/categories"
 -}
 -
 -list_courses() {
 -  local file name cachefile="${_DATA}/cache/courses.ui.cache"
 -  if [ $cachefile -nt "${_DATA}/ical" ]; then
 -    cat "$cachefile"
 -  else
 -    for file in "$_DATA/ical"/*.ics; do
 -      name="$(pdi_value "$(pdi_load "$file")" SUMMARY |HTML)"
 -      printf '%s      %s\n' "$file" "$name"
 -    done \
 -    | sort -k2 |tee "$cachefile"
 -  fi
 -}
 -
  w_filter_item() {
  n=$3
  cat <<EOF
    [fieldset .item
      [legend $(l10n filter_item):]
  
 -    $(for field in any name street zip TEL BDAY CATEGORIES course; do
 -      printf '[input id="%s%i" type="radio" name="filter_type%i" value="%s" %s][label for="%s%i" %s ]' \
 +    $(for field in any name street zip TEL BDAY; do
 +      printf '[input id="%s%i" type="radio" name="filter_type%i" value="%s" %s]
 +              [label for="%s%i" %s ]' \
                "$field" "$n" "$n" "$field" "$([ "$1" = "$field" ] && printf checked )" \
                "$field" "$n" "$(l10n filter_$field)"
      done)
 -    [input type="text" name="filter_text$n" value="$([ "$1" = CATEGORIES -o "$1" = course ] || HTML "$2")" placeholder="$(l10n filter_placeholder)"]
 -    [fieldset .categories
 -      $(list_categories | while read cat; do
 -          printf '[label [checkbox "filter_cat%i" "|%s" %s] %s ] ' \
 -                 "$n" "$(HTML "$cat")" \
 -                 "$(printf %s "$cat" |grep -qxEe "$2" && printf checked )" \
 -                 "$(HTML "$cat")"
 -      done)
 -      [a href="/categories/" $(l10n edit_categories)]
 -    ]
 -    [fieldset .courses
 -      $(list_courses | while read course coursename; do
 -        printf '[label [checkbox "filter_course%i" "|%s" %s] %s ] ' \
 -               "$n" "$(HTML "${course##*/}")" \
 -               "$(printf %s "${course##*/}" |grep -qxEe "$2" && printf checked )" \
 -               "$coursename"
 -      done)
 -    ]
 +    [input type="text" name="filter_text$n" value="$([ "$1" = CATEGORIES ] || HTML "$2")" placeholder="$(l10n filter_placeholder)"]
    ]
  EOF
  }
@@@ -44,21 -77,37 +44,22 @@@ w_filter_diag()
          w_filter_item "${fil%%:*}" "${fil#*:}" $n
          n=$((n + 1))
        done
-       w_filter_item any '' $n
+       [ "$n" -eq 0 -o "$(GET newfilter)" ] && w_filter_item any '' $n
      )
-   
+     [button type="submit" name="choice" value="new_filter" $(l10n filter_more)]
      [fieldset class="order"
        [legend $(l10n filter_order):]
 -      [label [radio "order" "firstname" $( [ "$order" = firstname ] && printf checked )] $(l10n filter_firstname)]
        [label [radio "order" "lastname"  $( [ "$order" = lastname  ] && printf checked )] $(l10n filter_lastname)]
 +      [label [radio "order" "firstname" $( [ "$order" = firstname ] && printf checked )] $(l10n filter_firstname)]
        [label [radio "order" "bdate"     $( [ "$order" = bdate     ] && printf checked )] $(l10n filter_bdate)]
      ]
-     [button type="submit" name="choice" value="new_filter" $(l10n filter_apply)]
+     [button type="submit" name="choice" value="filter" $(l10n filter_apply)]
      [button type="submit" name="choice" value="del_filter" $(l10n filter_cancel)]
+     [button type="submit" name="choice" value="export_csv" $(l10n export_csv)]
    ]
  EOF
  }
  
 -# listcards |grep ${edit:+-v} "$edit" \
 -# | while read card; do
 -#   "${_EXEC}"/cgilite/html-sh.sed <<-ENDCARD
 -#     [div #${card} .card
 -#       $(view_card "$card")[!--
 -#       --][div .control
 -#         [a "?action=edit_card&card=${card}" .item $(l10n edit)]
 -#         [a "?action=export_vcard&card=${card}".item $(l10n vcf_export)]
 -#         ${profile_medical:+[a "?action=new_prescription&client=${card}" .item $(l10n new_prescription)]}
 -#     ]]
 -#     ENDCARD
 -# done
 -
 -#!/bin/sh
 -
  card_item(){
    local card="$1"
    local item cnt c
      cnt="$(pdi_count "$card" "$item")"
  
      case $item in
-       FN) printf '[h2 .item .FN . %s]' "$(pdi_value "$card" FN |pdi_unescape |HTML)"
+       FN) printf '[h2 .item .FN . %s]' "$(pdi_value "$card" FN |unescape |HTML)"
          ;;
        GENDER) printf '[span .item .GENDER . %s]' "$(pdi_value "$card" GENDER |l10n)"
          ;;
        NICKNAME) seq 1 $cnt |while read c; do
-           printf '[span .item .NICKNAME aka. "%s"]' \
-                  "$(pdi_value "$card" NICKNAME $c |pdi_unescape |HTML)"
+           printf '[span .item .NICKNAME aka. "%s"]' \
+                  "$(pdi_value "$card" NICKNAME $c |unescape |HTML)"
          done
          ;;
        X-ZACK-JOINDATE|X-ZACK-LEAVEDATE) if [ $cnt -gt 0 ]; then
          [ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n EMAIL)"
          seq 1 $cnt |while read c; do
            printf '[a .item .EMAIL href="mailto:%s" . %s]' \
-                  "$(pdi_value "$card" EMAIL $c |pdi_unescape |HTML)" \
-                  "$(pdi_value "$card" EMAIL $c |pdi_unescape |HTML)"
+                  "$(pdi_value "$card" EMAIL $c |unescape |HTML)" \
+                  "$(pdi_value "$card" EMAIL $c |unescape |HTML)"
          done
          ;;
        TEL)
            [ "$teltype" ] \
            && printf '[span .item .TEL [span .type . %s:] %s]' \
                      "$(l10n "TYPE=$teltype" |HTML)" \
-                     "$(pdi_value "$card" TEL $c |pdi_unescape |HTML)" \
+                     "$(pdi_value "$card" TEL $c |unescape |HTML)" \
            || printf '[span .item .TEL . %s]' \
-                     "$(pdi_value "$card" TEL $c |pdi_unescape |HTML)"
+                     "$(pdi_value "$card" TEL $c |unescape |HTML)"
          done
          ;;
-                  ' "$(pdi_unescape "$hi_name" |HTML)" \
-                    "$(l10n hi_number)" "$(pdi_unescape "$hi_number" |HTML)" \
-                    "$(l10n hi_status)" "$(pdi_unescape "$hi_status" |HTML)"
 +      X-HEALTH-INSURANCE)
 +        [ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n X-HEALTH-INSURANCE)"
 +        seq 1 $cnt |while read c; do
 +          IFS=\; read -r hi_name hi_number hi_status <<-EOF
 +              $(pdi_value "$card" X-HEALTH-INSURANCE $c)
 +              EOF
 +          printf '[span .item .hi_company . %s]
 +                  [span .item .hi_number [label %s:] %s]
 +                  [span .item .hi_status [label %s:] %s]
++                 ' "$(unescape "$hi_name" |HTML)" \
++                   "$(l10n hi_number)" "$(unescape "$hi_number" |HTML)" \
++                   "$(l10n hi_status)" "$(unescape "$hi_status" |HTML)"
 +        done
 +        ;;
 +      therapies)
 +        client="$(pdi_value "$card" UID)"
 +        printf '[h3 %s]' "$(l10n therapies)"
 +        (cd "$_DATA/therapies/"; printf '%s\n' "${client}".*.tpy) \
 +        | while read tpyfile; do
 +          [ "$tpyfile" = "${client}.*.tpy" ] \
 +          && printf '[a .item .therapy href="/therapies/%s/new" . +]' "${client}" \
 +          && break
 +          tpy="${tpyfile%.tpy}";
 +          tpydates="$(sed -En 's;^session[0-9]+_date:;;p;' "$_DATA/therapies/$tpyfile" \
 +                      | sort \
 +                      | sed -E ':X;N;$!bX; s;^[\n ]+;;; s;[\n ]+$;;; s;(\n.*\n|\n); - ;;'
 +                     )"
 +          printf '[a .item .therapy href="/therapies/%s" . %s] ' \
 +                 "${tpy%.*}/${tpy#*.}" "$(HTML "${tpydates:--}")"
 +        done |sort -n
 +        ;;
        *)[ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n "$item")"
 +        shy="$(printf '\302\255')"
          seq 1 $cnt |while read c; do
            printf '[span .item .%s . %s]' "$item" \
-                  "$(pdi_value "$card" "$item" $c |sed -r "s;(straße|weg|damm|allee|ufer);${shy}\1;g" |pdi_unescape |HTML)"
 -                 "$(pdi_value "$card" "$item" $c |unescape |HTML)"
++                 "$(pdi_value "$card" "$item" $c |sed -r "s;(straße|weg|damm|allee|ufer);${shy}\1;g" |unescape |HTML)"
          done
          ;;
      esac
@@@ -176,9 -193,8 +177,8 @@@ edit_item()
                $N
                EOF
          else
-         N="$(pdi_value "$card" FN |pdi_unescape)"
-           n1="${N%%[a-z]*}" n1="${N#$n1}"
-           [ "$n1" ] || n1="${N##* }"
+         N="$(pdi_value "$card" FN |unescape)"
+           n1="${N##* }"
            n2="${N%$n1}"
          fi
          printf '
          [input .item .N name="5N" placeholder="%s" value="%s"]
          ' "$(l10n "$item")" \
          "$(l10n n_pre)"   "$(HTML "$n4")" \
-         "$(l10n n_first)" "$(HTML "${n2}${n3:+ }${n3}")" \
+         "$(l10n n_first)" "$(HTML "${n2}$([ "$n2" -a "$n3" ] && printf ' ')${n3}")" \
          "$(l10n n_last)"  "$(HTML "$n1")" \
          "$(l10n n_post)"  "$(HTML "$n5")"
          ;;
            printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
              "$item" $c "$item" $c "$item" $c "$(l10n delete)"
            printf '<textarea class="item %s" name="%s">%s</textarea>' \
 -            "$item" "$item" "$(pdi_value "$card" "$item" $c |unescape |HTML)"
 +            "$item" "$item" "$(pdi_value "$card" "$item" $c |pdi_unescape |HTML)"
          done
          printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
          ;;
                    "$([ "$teltype" = 'FAX'  ] && printf 'selected="selected"')" "$(l10n TYPE=FAX)"
  
            printf '[input .item .%s name="%s" value="%s" placeholder="%s"]' \
 -            "$item" "$item" "$(pdi_value "$card" "$item" $c |unescape |HTML)" "$(l10n "$item")"
 +            "$item" "$item" "$(pdi_value "$card" "$item" $c |pdi_unescape |HTML)" "$(l10n "$item")"
          done
          printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
          ;;
 +      X-HEALTH-INSURANCE)
 +        printf '[h3 %s]' "$(l10n "$item")"
 +        seq 1 $cnt |while read c; do
 +          # printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
 +          #   "$item" $c "$item" $c "$item" $c "$(l10n delete)"
 +          IFS=\; read -r hi_name hi_number hi_status <<-EOF
 +              $(pdi_value "$card" X-HEALTH-INSURANCE $c)
 +              EOF
 +          cat <<-EOF
 +              [input type="radio" name="$item" value="list" #hi_select_list checked]<!--
 +              -->[label for="hi_select_list" $(l10n hi_from_list)]<!--
 +              -->[input type="radio" name="$item" value="other" #hi_other checked]<!--
 +              -->[label for="hi_other" $(l10n hi_other)]<!--
 +              -->[select class="item" name="hi_company"
 +                [option value="" disabled="disabled" $(selected "${hi_name}" "") . $(l10n hi_company)]
 +                $(list_hi_companies |while read f; do
 +                  printf '[option value="%s" %s . %s]' "$(pdi_unescape "$f" |HTML)" \
 +                                                       "$(selected "$f" "$hi_name")" \
 +                                                       "$(pdi_unescape "$f" |HTML)"
 +                done)
 +              ]
 +              [input type="text" name="hi_other" value="$hi_name" placeholder="$(l10n hi_company)"]
 +              [input name="hi_number" value="$(pdi_unescape "$hi_number" |HTML)" placeholder="$(l10n hi_number)"]
 +              [input name="hi_status" value="$(pdi_unescape "$hi_status" |HTML)" placeholder="$(l10n hi_status)"]
 +              EOF
 +        done
 +        ;;
        *)printf '[h3 %s]' "$(l10n "$item")"
          seq 1 $cnt |while read c; do
            printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
              "$item" $c "$item" $c "$item" $c "$(l10n delete)"
            printf '[input .item .%s name="%s" value="%s" placeholder="%s"]' \
 -            "$item" "$item" "$(pdi_value "$card" "$item" $c |unescape |HTML)" "$(l10n "$item")"
 +            "$item" "$item" "$(pdi_value "$card" "$item" $c |pdi_unescape |HTML)" "$(l10n "$item")"
          done
          printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
          ;;
diff --combined index.cgi
index 0d85c87a1c13949361965d54ae42c2154a5e5551,ee0d6f7200900df4e43ba1a2347da90fca72006b..40db42b99eb53288fb2494aa7faac2947ee47acd
+++ b/index.cgi
@@@ -3,26 -3,31 +3,42 @@@
  for n in "$@"; do case ${n%%=*} in
    data) _DATA="${n#data=}";;
    exec) _EXEC="${n#exec=}";;
-   noerr) exec 2>&-;;
+   debug) DEBUG="${n#debug=}";;
  esac; done
  
- [ -z "${_EXEC%/}" ] && _EXEC="$(realpath "${0%/*}")" || _EXEC="${_EXEC%/}"
- [ -z "${_DATA%/}" ] && _DATA=. || _DATA="${_DATA%/}"
+ [ ! "${_EXEC%/}" ] && _EXEC="$(realpath "${0%/*}")" || _EXEC="${_EXEC%/}"
+ [ ! "${_DATA%/}" ] && _DATA=. || _DATA="${_DATA%/}"
+ [ "$DEBUG" ] && exec 2>>"$DEBUG"
+ mkdir -p "${_DATA}/cache" "${_DATA}/mappings" "${_DATA}/export" "${_DATA}/lock" "${_DATA}/ical" "${_DATA}/vcard"
+ debug() {
+   local dbg=/dev/stderr
+   if [ ! "$DEBUG" ]; then
+     [ "$#" -gt 0 ] && : || cat;
+   elif [ "$#" -gt 0 ]; then
+     printf '%s\n' "$@" >>"$dbg"
+   else
+     tee -a "$dbg"
+   fi
+ }
  
 +debug() {
 +  local dbg=/dev/stderr
 +  if [ ! "$DEBUG" ]; then
 +    [ "$#" -gt 0 ] && : || cat;
 +  elif [ "$#" -gt 0 ]; then
 +    printf '%s\n' "$@" >>"$dbg"
 +  else
 +    tee -a "$dbg"
 +  fi
 +}
 +
  . "$_EXEC/cgilite/cgilite.sh"
  . "$_EXEC/cgilite/session.sh"
  
+ . "$_EXEC/l10n.sh"
  _PATH="$(PATH "/${PATH_INFO}")"
  ACTION="$(GET a)"
  
@@@ -45,7 -50,7 +61,7 @@@ yield_page() 
    printf 'Content-Type: text/html; charset=utf-8\r\n\r\n'
    { printf '
        [html [head
 -          [title Confetti]
 +          [title Lobster]
          [meta name="viewport" content="width=device-width"]
          [link rel="stylesheet" type="text/css" href="/style.css"]
      '
@@@ -55,8 -60,9 +71,9 @@@
      printf '
        ] [body #top class="%s"
      ' "$class"
+     printf '[ul .menu [li [a "/cards/" . %s]][li [a "/courses/" . %s]]]' "$(l10n cards)" "$(l10n courses)"
+     [ "$message" ] && printf '[p #message\n%s\n]' "$(l10n "$message")"
      cat
-     [ "$message" ] && printf '[p #message\n%s\n]' $(l10n "$message")
      printf '] ]'
    } \
    | "${_EXEC}/cgilite/html-sh.sed"
  topdir="${_PATH#/}"
  topdir="/${topdir%%/*}"
  
- if [ "${_PATH}" = / ]; then
-   REDIRECT /cards/
- elif   [   -d "${_EXEC}/${_PATH}" -a -x "${_EXEC}/${_PATH}/index.cgi" ]; then
-   . "${_EXEC}/${_PATH}/index.cgi"
- elif [ ! -d "${_EXEC}/${_PATH}" -a -x "${_EXEC}/${_PATH}" ]; then
-   . "${_EXEC}/${_PATH}"
- elif [ ! -x "${_EXEC}/${_PATH}" -a -r "${_EXEC}/${_PATH}" ]; then
-   . "$_EXEC/cgilite/file.sh"
-   FILE "${_EXEC}/${_PATH}"
- elif   [   -d "${_EXEC}/${topdir}" -a -x "${_EXEC}/${topdir}/index.cgi" ]; then
-   . "${_EXEC}/${topdir}/index.cgi"
- else
-   printf 'Status: 404 Not Found\r\nContent-Length: 0\r\n\r\n'
- fi
+ case ${_PATH} in
+   /) REDIRECT /cards/
+     ;;
 -  /export/*.pdf) . "$_EXEC/cgilite/file.sh"
 -    FILE "${_DATA}/${_PATH}" "application/pdf"
 -    ;;
 -  /export/*) . "$_EXEC/cgilite/file.sh"
 -    FILE "${_DATA}/${_PATH}"
 -    ;;
++#  /export/*.pdf) . "$_EXEC/cgilite/file.sh"
++#    FILE "${_DATA}/${_PATH}" "application/pdf"
++#    ;;
++#  /export/*) . "$_EXEC/cgilite/file.sh"
++#    FILE "${_DATA}/${_PATH}"
++#    ;;
+   *)
+     if   [ -d "${_EXEC}/${_PATH}" -a -x "${_EXEC}/${_PATH}/index.cgi" ]; then
+       . "${_EXEC}/${_PATH}/index.cgi"
+     elif [ -f "${_EXEC}/${_PATH}" -a -x "${_EXEC}/${_PATH}" ]; then
+       . "${_EXEC}/${_PATH}"
+     elif [ -f "${_EXEC}/${_PATH}" -a -r "${_EXEC}/${_PATH}" ]; then
+       . "$_EXEC/cgilite/file.sh"
+       FILE "${_EXEC}/${_PATH}"
+     elif [ -d "${_EXEC}/${topdir}" -a -x "${_EXEC}/${topdir}/index.cgi" ]; then
+       . "${_EXEC}/${topdir}/index.cgi"
+     else
+       printf '%s\r\n' 'Status: 404 Not Found' 'Content-Length: 0' ''
+     fi
+     ;;
+ esac
diff --combined pdiread.sh
index c2654b68798fdb250aca46c0336b3f9d29012303,08fbaec3fb373c2fbb6cdde7974caaa520b849e3..f547cb689c1857d4397e9d7565cedc4f3ba0000a
@@@ -26,13 -26,23 +26,23 @@@ include_pdi="$0
  BR='
  '
  
+ unescape() {
+   local unescape='s;(^(\\\\)*|[^\\](\\\\)*)\\n;\1\n;g; s;\\(.);\1;g'
+   if [ $# -eq 0 ]; then
+     sed -E "$unescape"
+   else
+     printf %s "$*" \
+     | sed -E "$unescape"
+   fi
+ }
  pdi_load() {
    # normalise PDI file for processing with pdi_* functions
    # functions in this library can only be applied to normalised data
    # Usage example:
    # data="$(pdi_load file.vcf)"
  
-   sed -En '
+   sed -srn '
      # === Read entire file into buffer ===
      :X $bY; N; bX; :Y s;^.*$;\n&\n;;
  
      # === Update obsolete LABEL property ===
      s;\nLABEL((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)\n;\nADR\1\;LABEL="\5":\n;g;
  
-     p;' "$1"
+     p;' "$@"
  }
  
 +pdi_escape(){
 +  local in out=''
 +  for in in "$@"; do
 +    out="${out}${out:+;}"
 +    while [ "$in" ]; do case $in in
 +      \\*) out="${out}\\\\"; in="${in#\\}" ;;
 +      ,*) out="${out}\\,"; in="${in#,}" ;;
 +      \;*) out="${out}\\;"; in="${in#;}" ;;
 +      "$BR"*) out="${out}\\n"; in="${in#${BR}}" ;;
 +      *) out="${out}${in%%[\\,;${BR}]*}"; in="${in#"${in%%[\\,;${BR}]*}"}" ;;
 +    esac; done
 +  done
 +  printf '%s\n' "$out"
 +}
 +
 +pdi_unescape(){
 +  local in out=''
 +  [ $# -gt 0 ] && in="$*" || in="$(cat)"
 +  while [ "$in" ]; do case $in in
 +    \\\\*) out="${out}\\"; in="${in#\\\\}" ;;
 +    \\n*) out="${out}${BR}"; in="${in#\\n}" ;;
 +    \\*) in="${in#\\}" ;;
 +    *) out="${out}${in%%\\*}"; in="${in#"${in%%\\*}"}" ;;
 +  esac; done
 +  printf '%s\n' "$out"
 +}
 +
  pdi_count(){
    local card="$1" name="$2" rc='' cnt=0
    while rc="${card#*${BR}${name};}"; do