Package: alpine
Version: 2.02+dfsg-2
Severity: important

Hi,

on a UTF-8 system, a UTF-8 file such as the attached one
is corrupted by alpine when reading the eMail. This is
*not* #461916 because UTF-8 is my system’s default encoding.

What I do is:

• compose an eMail
• attach the file mksh-assockit.txt
• send that eMail
• go to my sent-mail folder
• see this:

  ALPINE 2.02(1266)   ATTACHMENT INDEX                    <UGS> sent-mail       
             Msg  99 of 100     +

   1   ~15 lines   Text/PLAIN
   2   ~10 KB      Text/PLAIN (Name: "mksh-assockit.txt")

• go to the text attachment
• see this:

????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??? Codeeinsendung: assockit ??? moderne Datenstrukturen f??r die Shell ???
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

Das Ausgangsproblem stellte sich wie folgt dar:

??? Die Public Domain Korn Shell stellt eine Programmiersprache zur
  Verf??gung, die neben den POSIX Shell Features auch Arrays beherrscht.
[…]

• Analyse the output; indeed, alpine inserts U+003F ‘?’ for
  every nōn-ASCII octet
• cry because I deleted the file locally (as I’ve got a backup
  copy in my sent-mail folder)
• ssh home
• using pine 4.64L on MirBSD, use the direct imap (G)oto command
  to access my work sent-mail folder
• see this:

  PINE 4.64L   ATTACHMENT INDEX 
...t.de:993/ssl/user=t.gla...@tarent.de}INBOX/sent-mail  Message 99 of 100      
+

   1   ~14 lines   Text/PLAIN
   2   ~10 KB      Text/PLAIN (Name: "mksh-assockit.txt"), ""

• go to the text attachment
• see this:

  PINE 4.64L   ATTACHED TEXT 
....de:993/ssl/user=t.gla...@tarent.de}INBOX/sent-mail  Message 99 of 100 16%   
   +

┌───────────────────────────────────�
��──────────────────────────────┐
│ Codeeinsendung: assockit – moderne Datenstrukturen für die Shell │
└───────────────────────────────────�
��──────────────────────────────┘

Das Ausgangsproblem stellte sich wie folgt dar:

① Die Public Domain Korn Shell stellt eine Programmiersprache zur
  Verfügung, die neben den POSIX Shell Features auch Arrays beherrscht.
[…]

• am not surprised about the bad line breaks, as pine didn’t
  know about encodings at all
• hit S to save the attachment
• get the original file back
• rejoice

The problem here is that, with alpine, I could *not* get
the original file back – when (S)aving, the corrupted file
is stored instead. So I had to use software not packaged
in Debian, and non-free to boot, (although another MUA’d
probably have sufficed but I can’t use them) to get my
file back. Hence “important” priority.

KDE 4.10 Kontact from experimental could indeed read the
file, it turns out. So I hope my recipient can read it…
I don’t know if it is something in the encoding of the
eMail or a bug in alpine’s interpretation thereof.

-- System Information:
Debian Release: 7.0
  APT prefers unstable
  APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: i386 (i686)

Kernel: Linux 3.2.0-4-amd64 (SMP w/4 CPU cores)
Locale: LANG=C, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/mksh-static

Versions of packages alpine depends on:
pn  libc6             <none>
ii  libgssapi-krb5-2  1.10.1+dfsg-5
ii  libkrb5-3         1.10.1+dfsg-5
ii  libldap-2.4-2     2.4.31-1+nmu2
ii  libpam0g          1.1.3-9
ii  libssl1.0.0       1.0.1e-2
ii  libtinfo5         5.9-10
ii  mlock             8:2007f~dfsg-2

Versions of packages alpine recommends:
pn  alpine-doc  <none>

Versions of packages alpine suggests:
ii  aspell                          0.60.7~20110707-1
ii  postfix [mail-transport-agent]  2.10.0-3

-- no debconf information
┌──────────────────────────────────────────────────────────────────┐
│ Codeeinsendung: assockit – moderne Datenstrukturen für die Shell │
└──────────────────────────────────────────────────────────────────┘

Das Ausgangsproblem stellte sich wie folgt dar:

① Die Public Domain Korn Shell stellt eine Programmiersprache zur
  Verfügung, die neben den POSIX Shell Features auch Arrays beherrscht.
  Diese Arrays sind syntaktisch (auf eindimensionale Arrays) und
  funktional (Indizēs sind numerisch, von 0 bis 1023) stark begrenzt.
  Es handlet sich hierbei um „sparse arrays“, d.h. nicht benutzte
  Elemente belegen keinen Speicherplatz, allerdings sind sie intern
  als einfach verkettete Liste implementiert.

② JSON ist ein größtenteils portables Datenaustauschformat mit
  Typen, die man haben will: assoziative Arrays („Object“), indizierte
  (nicht-sparse) Arrays („Array“), Strings, Integers (und Floats, aber
  die sind ein anderes Thema).

③ Die MirBSD Korn Shell stellt eine Weiterentwicklung der pdksh dar.
  Durch begrenzte Manpower wurde der Wunsch auf eine Abbildung von
  multidimensionalen Arrays und assoziativen Arrays noch nicht in
  der Shell-Implementation umgesetzt; insbesonders für multidimensionale
  Arrays muß hier erst noch Syntax für geschaffen werden, da das Setzen
  von Attributen auf Arrays in Shell nicht eindeutig ist (historischer
  Ballast) und aber keine existierenden Skripte, die diese neuen Mög‐
  lichkeiten nicht nutzen, gebrochen werden sollen.

④ mksh begrenzt Arrayindizēs nicht auf [0;1023] sondern erlaubt den
  gesamten 32-Bit Raum. (In mksh ist aus Portabilitätsgründen jeder
  Integer 32-bittig, egal ob es sich um eine 32-Bit oder 64-Bit CPU
  handelt, damit die Skripte sich auf die Shell verlassen können.)
  Es sind intern immer noch verkettete Listen, aber auch nicht lang‐
  samer als bei pdksh (und die Umstellung auf Hashtables folgt).

⑤ Es existiert ein Proof of Concept für die Realisierung von JSON in
  PHP („minijson.php“ in Evolvis/FusionForge) sowie eine Implementation
  von Hashtables in der Implementation von mksh (allerdings aktuell nur
  für den schnellen Lookup von Variablennamen, Funktionsnamen usw. ver‐
  wendet – diese bietet sich für sowohl assoziative als auch indizierte
  sparse arrays an).

⑥ POSIX Shell besitzt „eval“.

Für ein fachliches Problem – Analyse eines lokalen APT-Paketrepository;
Vergleich mit Debian unstable, um zu sehen, welche Pakete out of date
sind, welche lokale Änderungen haben, usw. – pressierte der Wunsch nach
assoziativen, multidimensionalen sparse arrays.

Hierfür wurde das sogenannte „assockit“ entwickelt. Es handelt sich um
eine mksh-Bibliothek, wurde also in der Programmiersprache Shell, nicht
als Teil der C-Implementation der Shell, entwickelt und muß dementspre‐
chend ohne syntaktischen Zucker auskommen. Das „assockit“ stellt einen
Satz von Shell-Funktionen bereit, die Array-Zugriffe realisieren.

Hier ist ein Syntaxvergleich für einen einfachen Zugriff auf ein ein‐
dimensionales sparse indexed array, indem Strings geschrieben und ge‐
lesen werden (i ist 2, um Sinn zu machen):

┌───────────────────────────────┬───────────────────────────────┐
│ Korn Shell                    │ assockit                      │
├───────────────────────────────┼───────────────────────────────┤
│ set -A arrname -- foo         │ asso_sets foo arrname 0       │
│ arrname[2]=baz                │ asso_sets foo arrname 2       │
│ unset arrname[0]              │ asso_unset arrname 0          │
│ x=${arrname[i]}               │ x=$(asso_getv arrname $i)     │
└───────────────────────────────┴───────────────────────────────┘

Erklärung zu den einzelnen Zeilen:

1) assockit benötigt kein spezielles Setup („arrname“ ist jetzt
   ein Array, und, ach übrigens, schreib mal „foo“ ins erste
   Element), man kann direkt zuweisen

2) Zuweisung eines Strings (assockit ist typisiert); bei assockit
   kommt der Wert an erster Stelle, um die Verkettung von internen
   Aufrufen zu vereinfachen, danach die Keys

3) Ähnlich der Zuweisung, natürlich ohne Wert

4) Assockit braucht $i, um dort eine 2 stehen zu haben; in Shell
   ist das Subskript implizit ein arithmetischer Ausdruck (was
   später assoziative Arrays in Shell selber erschwert; man muß
   dann angeben, ob das Array indiziert oder assoziativ ist, und
   bei letzteren muß das Subskript als String interpretiert werden)

Natürlich kann assockit viel mehr als das; dieses Beispiel dient
lediglich zur Erläuterung der Syntaxunterschiede durch Mangel an
syntaktischem Zucker und Notwendigkeit der Bereitstellung durch
Shell-Funktionen.

Es gibt eine Sache, die in assockit vorgesehen ist, aber noch nicht
implementiert wurde, weil sie in allen Anwendungen, in denen es zur
Zeit eingesetzt wird, nicht nötig ist: die Umwandlung eines indizierten
Arrays in ein assoziatives unter Beibehaltung der Datenelemente (aus
Element 0 wird dann ein Element mit dem String-Key "0", usw). Dieses
sollte implementiert werden durch JSON-Serialisierung jedes Elements,
dann Import als Elemente eines JSON-Object; das ist aber viel Code,
und da es keinen User gab…

Das assockit selber befindet sich im CVS des MirOS-Projektes:
https://www.mirbsd.org/cvs.cgi/contrib/hosted/tg/assockit.ksh?rev=1.3

Die Beispielsanwendung liegt ebenfalls dort:
https://www.mirbsd.org/cvs.cgi/contrib/hosted/tg/deb/quinn-ls.sh?rev=1.12

Generischer LDAP-Parser (mit Nutzungsbeispiel ab Zeile 111):
https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=shellsnippets/shellsnippets.git;a=blob;f=mksh/assoldap.ksh;h=585459863e7f7fbca527fc8936c1906b75d3cbbc;hb=HEAD

Das assockit besteht im wesentlichen aus der Implementation einer
Hashtable im „open addressing“ oder „closed hashing“ Verfahren, wo,
wenn der Hash des Elements kollidiert, kein „Bucket“ verwendet wird
(wie bei java.util.Hashtable), der linear durchsucht würde, sondern
stattdessen mit Hilfe eines (an Python angelehnten) Kollisionsauflöse‐
algorithmus weitere Indizēs geprobt werden.

Die Berechnung des Hashes eines Strings wird durch die mksh-Erweiterung
${_e@#} realisiert; man könnte jedoch auch eine Funktion aufrufen; in
https://www.mirbsd.org/cvs.cgi/src/bin/mksh/dot.mkshrc?rev=1.79 finden
sich geëignete Implementationen von Jenkins’ one-at-a-time Hash, der
auf 32 Bit abbildet und ein gutes avalanche behaviour hat (allerdings
nutzt die Implementation dieses Hashes auch eine mksh-Erweiterung, die
es allerdings schon viel länger gibt, nämlich unsigned int arithmetics).

Alles weitere geschieht durch komplexe Lookup-Verfahren, in denen jede
Hierarchie für sich funktioniert, im wesentlichen wie folgt:

Es gibt ein Top-Level assockit Array, dessen Name einfach leer ist.
Zu jedem assockit Array gehören zwei Shell-Arrays mit den Namen
Asso_*_f und Asso_*_k, wo das Sternchen jeweils durch den assockit-
Namen ersetzt wird. Das _f Shell-Array enthält den Typ, und das _k
Array enthält den Wert.

Bei einem Lookup wird für jede Hierarchie zunächst der Typ und der
Wert ermittelt; ist der Typ skalar (String, Zahl, Wahrheitswert,
JSON-null), wird er zurückgegeben, ansonsten steht im Wertearray
etwas, das an den assockit-Namen _angehängt_ wird, um die Verschach‐
telung zu ermöglichen. Sämtliche durch den User kontrollierten Arrays
werden so auf Shell-Arrays Asso_* gemappt, deren Namen durch „eval“
konstruïert und immer länger werden, je tiefer die Hierarchie.
Ein Beispiel hierzu befindet sich in den Kommentaren am Anfang der
assockit.ksh Datei.

Das assockit ist komplett in Pure mksh™ geschrieben, das bedeutet,
es nutzt ausschließlich die Shell und ruft niemals irgendeinen
Betriebssystemsbefehl auf. Hierdurch funktioniert es auch z.B. auf
Android oder mit dem experimentiellen Port von mksh auf Native WinAPI,
also wo mksh als reine Programmiersprache außerhalb der Unix-Umgebung
genutzt wird.

assockit ist ein rapider Prototyp und wird entsorgt werden, sobald
mksh selber komplett neuen Array-Code enthält. Durch die Entwicklung
und Benutzung von assockit sowie begleitende Diskussion (inklusive
Ideën-Bouncing) mit mehreren anderen Entwicklern wurde das Konzept
zur Einbindung assoziativer und multidimensionaler Arrays in mksh
selber mehrfach komplett überworfen; die Implementation sollte also
deutlich weniger fehleranfällig werden.

Der LDAP-Parser erzeugt unterhalb des ihm gegebenen Argumentes eine
Struktur, in der als Top-Level-Array die dn (Distinguished Name) der
Einträge als Key dienen; jeder Eintrag ist nochmal ein assoziatives
Array, in dem die Key-Value-Paare von LDIF, nach Dekodieren, wieder‐
gegeben werden. (Shell-Strings können kein NUL enthalten, daher wird
ein jpegPhoto-Attribut nicht korrekt wiedergegeben, aber das ist ein
Spezialfall, und für den Rest tut’s gut.) Dieser generische Parser
kann die bisher jedes Mal, wenn es administrativ LDAP zu verarbeiten
galt, aufs Neue geschriebenen spezifischen LDAP-Parser ersetzen und
reduziert somit den Arbeitsaufwand durch Codewiederverwendung.

Reply via email to