Dienstag, 1. Dezember 2015

Selecting the schema of a Sqlite database

The interactive Sqlite command line tool offers the command .schema to read the Schema of a database. This works fine, but of course it is not a valid SQL query. The following query returns the whole schema in one string and is a valid SQL query.
SELECT group_concat(sql, ';' || char(10)) || ';'
FROM sqlite_master
WHERE name NOT LIKE 'sqlite_%';

Mittwoch, 14. Oktober 2015

Using the last command in the Bash prompt

Today I had the idea that it might be useful to have the return value of the last command in the PUtty title. And next step is to put also the last command into the title.

The command history 1 prints the last command from the Bash history of commands. The Output is prefixed by a number and two spaces. The following Bash expression removes the prefix and prints just the command.

$(h=$(history 1);n=${h/% */};echo "${h#$n }")

First h is initialized with the whole history line. Then n is set to the remaining part of h after the longest match from the right up the a space is removed from h. And in the end h without the prefix stored in n is printed.

Donnerstag, 8. Oktober 2015

Limit the length of the path in a Bash prompt

It is possible to define the Bash prompt by setting the PS1 variable. A typical prompt definition is a combination of user name, host name and working directory:

# export PS1='\u@\h:\w\$ '
root@server:/tmp/aaa/bbb/ccc/ddd/eee/fff/ggg/hhh/jjj/kkk# ▉

But this makes it difficult to dive into really deep directory tries. If the directory gets longer than the width of the terminal, the prompt will span two lines. And if you have to use a limited terminal on a Solaris system, the screen gets totally messed.

For this situations it is possible to define the prompt in that way, that only the last directory is part of the prompt.

# export PS1='\u@\h:\W\$ '
root@server:kkk# ▉

But having only one directory is often not enough. If you have some source packages, each containing a src directory, it not possible any more to distinguish them.

The best solution for the problem would be to clip the path to a maximum length and indicate the clipping by some kind of special character. That can be done by the following prompt definition.

# export PS1='\u@\h:$(p=${PWD: -30};p=${p:+<$p};echo ${p:-$PWD})\$ '
root@server:<cc/ddd/eee/fff/ggg/hhh/jjj/kkk# ▉

The definition uses Bash's parameter expansion feature in three steps. First a variable p is defined and 30 characters of PWD are taken from the right. If PWD is less than 30 characters p is an empty string. Otherwise p is the clipped part of PWD. In the next step p gets prefixed with the '<' character, if it is set. Otherwise it will be still undefined. And in the last step either p will be printed, if it is set, or PWD, if p is still undefined. This means that either the clipped and prefixed part of PWD gets printed, if it is longer than 30 characters, or PWD is printed without any modifications.

Montag, 28. September 2015

Comparing SunOS patches

On Solaris 10 it is sometimes necessary to compare the installed patches. The following command generates a list of all patches:
showrev -p |sed 's/^Patch: \([^ ]*\) .*/\1/'
And the following Perl script takes the output of the above command from two different systems and compares the patches. The script assumes, that the patches on the second system are newer.
#! /usr/bin/perl
use strict;
use warnings;

#showrev -p |sed 's/^Patch: \([^ ]*\) .*/\1/'

sub max {
    my $max  = shift;
    my $next = shift;
    return $max if not $next;
    unshift @_, $max > $next ? $max : $next;
    goto &max;
}

sub read_patches {
    my $file = shift;
    my $hash;
    while (<$file>) {
        s/\r?\n$//;
        my ($patch, $number) = split ('-');
        push @{$hash->{$patch}}, $number;
    }
    return $hash;
}

my ($f1, $f2) = @ARGV;

my ($fd1, $fd2);
open $fd1, "<", $f1 || die "Can not open input file $f1, $!";
open $fd2, "<", $f2 || die "Can not open input file $f2, $!";

my ($p1, $p2);
$p1 = read_patches ($fd1);
$p2 = read_patches ($fd2);

print $f1, ": ", scalar(keys %$p1), "\n";
print $f2, ": ", scalar(keys %$p2), "\n";

my ($p, $n1, $n2);
foreach $p (sort keys %$p2) {
    $n1 = max(@{$p1->{$p}});
    $n2 = max(@{$p2->{$p}});
    if (!defined $n1 || $n1 < $n2) {
        print $p, ": ", (defined $n1 ? $n1 : "()"), " -> ", $n2, "\n";
    }
}

Sonntag, 6. September 2015

Windows-Schriften unter Linux verwenden

Wer unter Windows eine virtuelle Maschine verwendet, um Linux zu benutzen, kann sehr einfach die Windows-Schriften auch unter Linux verwenden. Dazu muss lediglich das Verzeichnis, in dem sich unter Windows die Schriften befinden als Shared-Folder für den Linux-Gast freigegeben werden. Die entsprechende Option sieht VirtualBox folgendermaßen aus.

Mit Hilfe des Namens kann das Verzeichnis unter Linux eingebunden werden. Dazu muss erst ein Mount-Point angelegt werden.

mkdir /usr/local/share/fonts/windows

Anschließend kann ein Eintrag in der fstab-Datei hinzugefügt werden, damit das Verzeichnis beim Start automatisch eingebunden wird.

echo fonts /usr/local/share/fonts/windows vboxsf ro,comment=systemd.automount 0 0 >> /etc/fstab

Wer sein System nicht neu starten will, muss jetzt das Verzeichnis manuell einbinden.

mount fonts

Abschließend muss der Font-Cache aktualisiert werden. Das kann je nach Anzahl der Fonts unter Windows etwas dauern.

fc-cache -fv

Jetzt stehen die Schriften im System zur Verfügung und können in den entsprechenden Programm ausgewählt werden. Zum Beispiel Tahoma für XFCE ...

... oder Emacs mit Lucida Console wie damals auf den SPARCstations unter OpenWindows.

Nachtrag

Emacs dazu zu überreden, dass das richtige Font-Hinting verwendet wird, gestaltet sich schwieriger als erwartet. Der Font-Dialog erlaubt es nicht, das Font-Hinting auszuwählen. Wer es trotzdem verwenden will darf nicht über die Emacs-Customization die Schrift auswählen. Statt dessen muss sie als X11-Resource in der Datei ~/.Xresources definiert werden. Für Lucida-Console auf einem 72dpi-Display sieht der Eintrag folgendermaßen aus:

Emacs.font: Lucida Console-14:antialias=true:hinting=true:autohint=false:hintstyle=3

Bei Systemen mit einer höheren Auflösung kann man auch kleinere Schrift-Größen wählen.

Mittwoch, 12. August 2015

Absätze durch Zeilenumbrüche in MS-Word ersetzen

Wenn man Quellcode in Word einfügt, kommt es oft dazu, dass an jedem Zeilenende ein Absatz eingefügt wird. Wenn Absätze so formatiert sind, dass zwischen ihnen ein Leerraum sein muss, führt das dazu, dass man zwischen allen Code-Zeilen einen Abstand hat. Die Lösung für das Problem ist, den Absatz zu löschen und durch einen Zeilenumbruch (Shift-Return) zu ersetzen. Bei längeren Listings ist das ein sehr nerviger Vorgang.

Man kann sich das Problem vereinfachen, indem man die Suchen-und-Ersetzten-Funktion von Word nutzt. Ein Absatz ist in Word die Zeichen-Sequenz "^p" und ein Zeilenumbruch "^l". Damit kann man in dem betreffenden Code-Block alle Absätze durch Zeilenumbrüche ein einem Rutsch ersetzen.

Donnerstag, 8. Januar 2015

Langsamer ist besser!

Die Programmiersprache C gilt als super schnell und optimal geeignet für Hardware-nahe Aufgaben. Im Internet gibt es reichlich Verweise darauf, wie viel schneller C ist und wie viel langsamer im Gegensatz dazu interpretierte Sprachen sind. Das Problem an solchen Vergleichen ist, dass sie Äpfel mit Birnen vergleichen. Das wird deutlich, wenn man sich das folgende Beispiel ansieht.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)
{
  int8_t a, b, c;

  a = atoi (argv[1]);
  b = atoi (argv[2]);

  c = a + b;

  printf ("%d\n", c);

  return 0;
}

Das Programm erwartet zwei Zahlen als Argument, addiert sie und gibt das Ergebnis aus. Es lässt sich ohne Fehler und Warnungen übersetzen.

cc -Wall -c -o c-speed.o c-speed.c
cc c-speed.o -o c-speed
Und auf den ersten Blick scheint es ganz gut zu funktionieren.

$ ./c-speed 1 2
3
Auf den zweiten Blick auch noch.

$ ./c-speed 10 20
30

Nur auf den dritten leider nicht mehr.

$ ./c-speed 100 200
44

Das Problem ist, dass für die Speicherung der Zahl 8 Bit verwendet wurden. Integer-Zahlen werden von heutzutage üblichen Architekturen als Zweierkomplement gespeichert. Das bedeutet, dass ein Bit für das Vorzeichen reserviert wird und die restlichen für die Zahl. Der Wertebereich ist somit -128 bis 127. Darin lässt sich eine 100 speichern aber keine 200 und erst recht keine 300.

Die binäre Darstellung der drei Zahlen sieht folgendermaßen aus.

100 ⇒ 0 1 1 0 0 1 0 0
200 ⇒ 0 1 1 0 0 1 0 0 0
300 ⇒ 0 1 0 0 1 0 1 1 0 0

Auf der rechten Seite ist das niedrigstwertige Bit und auf der linken das höchstwertige Bit. Die führende Null gibt an, dass es sich um eine positive Zahl handelt. Für die 200 reichen somit 8 Bit nicht mehr aus, es werden 9 benötigt. Und für die 300 werden schon 10 benötigt.

C ist dieser Sachverhalt völlig egal. Statt den Überlauf zu erkennen wird einfach mit dem weiter gerechnet, was übrig bleibt nachdem die übergelaufenen Bits weggeschmissen wurden. Dafür gibt es auch eine hochtrabende Bezeichnung: rechnen in Restklassenringen. Und das ist etwas völlig anderes als das, was Schülern in Unter- und Mittelstufe als "Rechnen" beigebracht wird.

In diesem konkreten Fall bedeutet es, dass das neunte Bit der 200 und das neunte und zehnte Bit der 300 über Bord gehen. Allerdings kommt es bei der 300 dazu gar nicht erst, weil durch den Wegfall des neunten Bits die 200 negativ wird, weil jetzt das höchste Bit nicht mehr eine Null sondern eine Eins ist.

200 ⇒ 0 1 1 0 0 1 0 0 0
1 1 0 0 1 0 0 0 ⇒ -56

Somit entsteht bei der Addition gar kein Überlauf, da C aus der Addition eine Subtraktion macht: 100 + (-56) = 44. Somit wäre erklärt wie C bei der Addition von 100 und 200 auf 44 kommt. Und da C keine Addition sondern eine Restklassen-Addition durchgeführt hat, hat es auch alles richtig gemacht. Dass weder das Restklassen-Ergebnis noch die Erklärung, was genau es denn ist, einem normal sterblichen Anwender irgend etwas bringen, der auf seinem Kontoauszug eine 44 liest, obwohl er gerade 200€ eingezahlt hat und zuvor noch 100€ auf dem Konto waren, dürfte offensichtlich sein.

Ein typischer C-Programmierer wird nun darauf hinweisen, dass man ja auch mehr Bits für die Speicherung von Zahlen verwenden kann. Die Zeiten, dass man mit Prozessoren auskommen musste, die nur 8 Bit verarbeiten konnten sind lange vorbei und somit könnte man zur Speicherung der Zahl int16_t oder int32_t oder int64_t verwenden. Schon int16_t würde für die Speicherung von 100, 200 und 300 völlig ausreichen, da für die 300 nur 10 Bit nötig waren.

Auf den ersten Blick erscheint das einleuchtend aber das ist nicht wirklich eine Lösung für das Problem. Das Problem ist lediglich aufgeschoben und aufgeschoben ist bekannter Weise nicht aufgehoben. Die Aufschiebung besteht darin, dass der Überlauf bei der Verwendung von int16_t nicht mehr bei Zahlen größer als 127 auftritt sondern bei Zahlen größer als 32767. Das Spielchen kann man jetzt weiter treiben und immer größere Wortbreiten verwenden.

8 Bit:127
16 Bit:32767
32 Bit:2147483647
64 Bit:9223372036854775807
128 Bit:170141183460469231731687303715884105727
256 Bit:57896044618658097711785492504343953926634992332820282019728792003956564819967

Die 256 Bit erscheinen auf den ersten Blick recht groß und man könnte auf die Idee kommen, die Wahrscheinlichkeit, dass es in einem Programm, das mit 256 Bit Zahlen arbeitet, zu einem Überlauf kommt, könne man jetzt wirklich vernachlässigen.

Allerdings sollte man sich klar machen, dass es durchaus Anwendungen gibt, bei denen längere Zahlen benötigt werden. Eine RSA-Verschlüsselung mit 256 Bit Zahlen ist heutzutage als lächerlich zu betrachten, da zur Zeit mindestens 2048 Bits benötigt werden, um einigermaßen sicher zu sein. Und ebenso wichtig dürfte der Sachverhalt sein, dass man nicht davon ausgehen darf, dass ein Programm in dem vom Programmierer vorgesehenen Zahlenbereich arbeitet. Jemand, der eine Sicherheitslücke in einer Software aufspüren will, wird selbstverständlich ein Programm gerade mit solchen Eingaben füttern, die außerhalb dessen liegen, was der Programmierer der Software ursprünglich bei der Planung und Entwicklung als normal angesehen hatte.

Das bedeutet, dass das typische Verhalten eines C-Programmierers, Zahlenüberläufe zu ignorieren, ein sehr großes Sicherheitsrisiko darstellt. Die Geschwindigkeit, die man dadurch erreicht, dass man einfach ungenau arbeitet, wird durch Fehler und Sicherheitsprobleme erkauft. Und somit ist die Aussage "C sei schnell" mehr ein Fluch als ein Segen.

Wenn man also Zahlenüberläufe wirklich vermeiden will, dann ist die logische Konsequenz, dass man eine Zahl nicht mehr in etwas speichern kann, das durch den Restklassenring der gerade zur Verfügung stehenden CPU beschränkt ist. Denn genau das macht C. Statt dessen muss man eine Zahl als eine sog. Bignum oder Big-Integer speichern. Das wiederum geht auch in C unter Verwendung der GMP-Bibliothek. Aber dadurch verliert man die sagenumwobene Geschwindigkeit von C, weil eine Zahl nicht mehr mit nur einem CPU-Befehl addiert werden kann. Statt dessen sieht die Rechnung mit Bignums folgendermaßen aus.

  1. Dekodierung der Zahl von der in der Bignum-Bibliothek gewählten Kodierung in eine für die verwendete CPU verständliche Darstellung.
  2. Durchführung der gewünschten Rechnung unter zu Hilfenahme von diversen von der CPU bereitgestellten Operationen.
  3. Codierung der Zahl in die von der Bignum-Bibliothek benötigte Form.
Und dieser Aufwand muss für jede Addition, jede Multiplikation und jede sonstige Rechenoperation gemacht werden. Und damit ist auch sofort klar, dass das alles andere als schnell ist sonder richtig langsam. Und das ist auch gut so. Denn in diesem Fall bedeutet Langsamkeit Richtigkeit.

Wer also der Meinung ist, in C zu programmieren sei eine gute Idee, weil die Programme so schön schnell sind oder die Verwendung des Java-Typs int sei eine gute Idee, weil er so viel schneller als BigInteger ist, dem kann ich nur mein Beileid aussprechen. Die Schnelligkeit von C ist keine Eigenschaft der Sprache, sondern man bezahlt für sie in barer Münze. Heutzutage kann man Exploits wie geschnitten Brot kaufen. Wer also sicher programmieren will, sollte darauf Wert legen, dass seine Programmiersprache keine Werbung damit macht, wie schnell sie sei. Statt dessen gilt beim Programmieren wie in der 30-Zone vor dem Kindergarten:

Langsamer ist besser!