Donnerstag, 13. November 2014

Apple 20 Jahre später und kein bischen besser

Ich bin jetzt leider dazu verdammt, ein Apple-Produkt zu benutzen. Seit 20 Jahren ist der Kelch zum Glück an mir vorüber gegangen aber jetzt gibt es in meiner Firma keine anderen Telefone mehr als solche von Apple. Der ersten Schock, an dessen Überwindung ich zur Zeit arbeite, hat mich direkt in die frühen 90er zurück katapultiert: das dumme iPhone hat keine Zurück-Taste. Schon damals hat Apple an den Tasten gespart und der Maus nur eine davon spendiert, während jede Workstation seit ewigen Zeiten drei Tasten hatte. Aber damals, anders als jetzt, war ich nicht dazu gezwungen, mit dem wenigen auskommen zu müssen, was Apple seinem typischerweise technisch beschränkten Klientel zuzumuten sich wagt.

Nachdem ich feststellen musste, dass auch die Musik noch die gleiche ist wie in den 90ern, konnte ich jedoch mit großem Erstaunen eine echte Veränderung beobachten: Mosaic heißt jetzt Safari. Von dem, was der neue iPhone-Browser kann, unterscheidet er sich allerdings wiederum kaum von dem aus den 90ern: Plugins gehen heute genauso wenig wie damals. Und damit wird es jetzt wirklich lästig. Denn anders als damals in den Anfängen des Internets, wo man private Vereine gründen musste, um überhaupt ohne einen Uni-Computer ins Internet kommen zu können, ist heute ein Leben ohne Browser-Plugins, zumindest für mich, nicht mehr vorstellbar. In den 90er gab es fast nur sinnvolle Inhalte im Internet, heute dagegen gibt es fast nur noch sinnlose Inhalte. Und deswegen sind die beiden wichtigsten aller Browser-Plugins Adblock und PwdHash: Adblock, um sich ungefragte Inhalte vom Leibe zu halten, und PwdHash, um sich unliebsame Zeitgenossen vom Leibe zu halten. Für beides gilt: Fehlanzeige auf einem iPhone!

Wenn man der Sache versucht auf den Grund zu gehen, stellt man fest, dass Apple anscheinend Browser-Plugins mit Gewalt verhindern will. Chrome für iPhone verfügt über keine Möglichkeit, Browser-Plugins zu verwenden und Firefox gibt es erst gar nicht. Und bei der erfolglosen Durchsicht der übrigen Browser-Varianten ist mir auch klar geworden, wo bei einem iPhone die Prioritäten liegen. Apple ist voll und ganz damit beschäftigt, die Bedürfnisse amerikanischer Prüderie zu befriedigen, und hat somit keine Zeit mehr, was Sinnvolles zu tun.

Man achte auf die folgende Klassifikation eines Internet-Browsers für das iPhone.

Dieser Browser verfügt über stark ausgeprägte Szenen mit erotischen Anspielungen, weswegen er erst mit 17 Jahren benutzt werden kann.

Eine viertel Stunde später: ich habe wieder eine trockene Hose an. Die alte musste ich leider wechseln, da ich mich vor lachen bepisst hatte.

Einem Browser zu attestieren, er sei Jugend gefährdend, ist ungefähr so hellsichtig wie einem Optiker zu unterstellen, er würde Prostitution fördern, weil man mit der Brille die Nutten erkennen kann. Aber gut, dass Apple uns das jetzt mal richtig klar gemacht hat. Und Brillen kaufen ist ja auch mitunter eine kostspielige Angelegenheit. Das Geld kann man ja viel besser in Apple-Produkte investieren. Das erinnert mich irgendwie an das Drama, eine Apple-ID ohne Kreditkarte zu bekommen. Aber das ist eine andere Geschichte...

Nun was ist das Fazit dieser Geschichte: ein Telefon bleibt ein Telefon und ist zu nichts mehr zu gebrauchen als zum telefonieren. Auch daran hat sich seit den 90er nichts geändert.

Sonntag, 9. November 2014

Combining the Bash nounset option with indirect expansion

Bash has a useful option called nounset. The option terminates the script if a variable is use, which has not been initialized. Using uninitialized values is a common error in script languages. Perl has introduced the strict package to address this kind of error and it is good Perl programing style to use it. The same applies to Bash: make set -u the first line after the shebang.

If you do so it becomes a bit more complicated to check, if a variable is set. The standard test will not work any more:

set -u
if [ -z "$VAR" ]; then
  echo 'VAR has no value.'
fi

Bash will terminate the script, because VAR is not set and there is no way to catch this exception. This means you have to avoid triggering nounset. This can be done with Bash's parameter expansion, in particular the "use default value" variant. The following code will not fail anymore:

set -u
if [ -z "${VAR:-}" ]; then
  echo 'VAR has no value.'
fi

Now the expression expands to the default value behind the hyphen, which is an empty string.

If you like to put the code into a function, it can be done this way:

set -u
require_value ()
{
  local VAR=$1
  local VAL=${!VAR}
  if [ -z "$VAL" ]; then
    die "Required value missing: $VAR"
  fi
}

The function require_value does another parameter expansion, the indirect expansion. The function takes the name of a variable as an argument and checks if the variable with the specified name has a value. This works as long as you do not enable the nounset option. If you enable it the function fails while setting the variable VAL.

The Bash manual is not very detailed about the question how to combine two parameter expansions. Trying to put them into each other fails.

$ a=b; echo ${${!a}:-}
bash: ${${!a}:-}: bad substitution

The answer is, that you can combine both notations into one parameter expansion and it happens just the right thing, which means the indirection is performed first and after that the default value gets applied.

set -u
require_value ()
{
  local VAR=$1
  local VAL=${!VAR:-}
  if [ -z "$VAL" ]; then
    die "Required value missing: $VAR"
  fi
}

Die Dreistigkeit der Placebo-Mafia

Unter der Domäne www.cialis20mgkaufen.net findet sich folgende schmucke Webseite.

Die Webseite betitelt sich selbst als "Geprüfter Deutscher Shop". Wenn man sich das Impressum der Seite ansieht, sieht man sofort, dass der Laden in der urdeutschen Gemeinde Venlo angesiedelt ist. Vielleicht war Deutschland in den Grenzen von Kaiser Karl dem Großen gemeint. Einem technisch versierten Beobachter fällt außerdem noch auf, dass das Impressum als Grafik hinterlegt ist. Dafür kann es eigentlich nur einen Grund geben: man will erreichen, dass die Information möglichst schlecht automatisch gelesen werden kann.

Wenn man sich dann den Inhalt der Whois-Datenbank für die DNS-Registrierung ansieht, stellt man fest, dass die Firma "Web Domains By Proxy" aus Pakistan die Domäne registriert hat. Man hat keine Mühen gescheut, seine Identität zu verschleiern.

Aber der größte Witz ist das Testergebnis von Stiftung Warentest. Angeblich wurden in Heft 06/2011 "Potenzmittel Online Apotheken aus Europa" getestet. Wer sich den Titel ausgedacht hat, sollte vielleicht beim Postillon anheuern. Wenn man sich also das tatsächliche Inhaltsverzeichnis von Heft 06/2011 ansieht, findet man den besagten Test natürlich nicht.

Vermutlich gilt das gleich für sämtliche anderen Logos auf der Seite: alles frei erfunden.

Sehr hübsch ist auch das Google+-Profil von unserem Apotheker.

Der gute Mann wohnt in Berlin und hat seine Versandlager laut Impressum in München, Venlo und UK. Die immense Reisetätigkeit, die eine solche Kombination nach sich zieht, ist vermutlich auch der Grund, warum er leider bislang noch keine Gelegenheit hatte, die deutsche Sprache zu lernen.

Wenn man dann das süße Profilfoto nimmt und bei Google eine Suche nach ähnlichen Bildern macht, findet man einen Dr. med. Ulrich Albers.

Man kann also mit ziemlicher Sicherheit sagen, dass auf der zitierten Webseite so ziemlich alles gelogen ist. Wer dort kauft, kann sein Geld auch direkt in den Gulli werfen. Ein Beispiel für die ganz alltägliche Internet-Abzocke.

Update

Eigenartiger Weise scheint Google ein Interesse daran zu haben, dass die Machenschaften von dubiosen Internet-Anbietern nicht veröffentlicht werden. Denn wie von Geisterhand sind die Screenshots, die ich von der dubiosen Webseite gemacht hatte, aus meinem Picasa-Konto gelöscht worden, ohne dass mich irgend jemand über diese Vorgänge informiert hat. Ich bin mal gespannt, ob die fleißige Bienchen, die sich für die Interessen von Internetbetrügern einsetzen, auch meinen Blog besuchen.

Montag, 3. November 2014

Emulating X11 selections on Windows

Putty

Putty can emulate the Xterm copy/paste style. It is not the default but it can easily activated

Emacs

In Emacs it is a bit more complicated. The following definitions are required in the .emacs file:

(setq select-active-regions nil)
(setq mouse-drag-copy-region t)
(global-set-key [mouse-2] 'mouse-yank-at-click)

Firefox

In Firefox the paste style can be activated in about:config. Search for "middle" and set middlemouse.paste to true.

In order to copy the selected text to the clipboard it is necessary to install an extension. AutoCopy 2 does the job.

Dienstag, 28. Oktober 2014

Scroll sections with Emacs

Lisp and Scheme programmers often use a form feed to separate source code sections. But this works not very well in other languages. Some people even try to proscribe the use of ^L. Xah Lee suggest the use of § instead. But the paragraph sign has also some limitations. First it is an Unicode symbol and this may cause problems, if you work with Redmond or Java disciples on the same source, because they are limited to UTF-16. Even Perl might be problematic, because Perl's default code page is Latin-1. And second it looks pretty ugly in the code. ;-)

An alternative is to teach Emacs how to recognize major comments. People tend to distinguish comments by varying the number of comment delimiters used to start the comment. In a Bash script it is enough to use one # to start a comment. But major comments are started by two number signs or even three. The same applies to Scheme hackers. They use two semicolons for comments at the beginning of the line, one semicolon for comments in the middle of the line and three for headlines. Instead of teaching Emacs how to find the paragraph sign, it would be much more convenient, if Emacs can jump between the major comments. The following code shows how to do it.

(defun forward-section ()
  "Move cursor forward to next occurrence of a major comment."
  (interactive)
  (if (search-forward-regexp "\n\n\\(#\\{2,\\}\\|[;/-]\\{3,\\}\\)" nil t)
      (beginning-of-line)
    (goto-char (point-max))))

(defun backward-section ()
  "Move cursor backward to previous occurrence of a major comment."
  (interactive)
  (let ((p (search-backward-regexp "\n\n\\(#\\{2,\\}\\|[;/-]\\{3,\\}\\)" nil t)))
    (goto-char (if p (+ p 2) (point-min)))))

(define-key global-map (kbd "<C-next>") 'forward-section)
(define-key global-map (kbd "<C-prior>") 'backward-section)

The example works for Bash (##), Scheme and Lisp (;;;), the C languages (///) and SQL (---).

Redirecting stdout and stderr into files without loosing the original file descriptors

Redirecting in Bash can be tricky. But in order to log the output of shell commands it is sometimes necessary. Sometimes it is also necessary to add some time stamps in front of the output. And sometimes it is even necessary to save the original file descriptors. The following example shows how to do all of that.

#! /bin/bash

stamp ()
{
  local LINE
  while IFS='' read -r LINE; do
    echo "$(date '+%Y-%m-%d %H:%M:%S,%N %z') $$ $LINE"
  done
}

exec {STDOUT}>&1
exec {STDERR}>&2
exec 2> >(exec {STDOUT}>&-; exec {STDERR}>&-; exec &>> stderr.log; stamp)
exec > >(exec {STDOUT}>&-; exec {STDERR}>&-; exec >> stdout.log; stamp)

for n in $(seq 3); do
  echo loop $n >&$STDOUT
  echo o$n
  echo e$n >&2
done

Some best practices followed in the code are:

  • Declare local variables as local.
  • Use named file descriptors to avoid collisions.
  • Close unused file descriptors in sub shells.

First I tied to solve my problem with the answers given at Stackexchange. But the answers where not elaborated enough for my needs. So I wrote the above answer.

BTW this is a nice overview for the different ways to redirect in Bash.

Sonntag, 26. Oktober 2014

Removing duplicate images with Chicken-Scheme

Because of an mouse accident I have several duplicate images. Windows adds " (2)" in front of the extension when two files have the same name. It looks like this:

The following script removes the useless duplicates.

(use posix)

(define (sysname)
  (let ((si (system-information)))
    (if (pair? si)
        (car si)
        #f)))

(define file-separator 
  (let ((sysname (sysname)))
    (cond
     ((equal? sysname "windows") "\\")
     (else "/"))))

(define (append-file-name a b)
  (string-append a file-separator b))

(define-syntax append-file-name*
  (syntax-rules ()
    ((_ a) a)
    ((_ a b) (append-file-name a b))
    ((_ a b ...) (_ a (_ b ...)))))

(define (prepend-directory-name directory-name file-names)
  (map (lambda (name)
         (append-file-name directory-name name))
       file-names))

(define (windows-duplicate name)
  (let ((index (string-index-right name #\.)))
    (if index
        (let ((basename (string-take name index))
              (extension (string-drop name index)))
          (string-append basename " (2)" extension))
        name)))

(define (duplicate? a b)
  (if (and (regular-file? a)
           (regular-file? b))
      (= (file-size a)
         (file-size b))
      #f))

(define (directory* name)
  (prepend-directory-name name (directory name)))

(define (find-duplicates dirname kind)
  (let loop ((files (directory* dirname))
             (duplicates (list)))
    (if (pair? files)
        (let ((name (car files))
              (rest (cdr files)))
          (if (directory? name)
              (loop rest (loop (directory* name)
                               duplicates))
              (let ((duplicate (kind name)))
                (if (duplicate? name duplicate)
                    (loop rest (cons duplicate duplicates))
                    (loop rest duplicates)))))
        duplicates)))

(for-each delete-file*
          (find-duplicates "C:\\Users\\Me\\Pictures\\Original\\2009"
                           windows-duplicate))