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.