PowerShell: Webcomic-Download-Script

Ich lese jeden Morgen in der Frühstückspause ein paar Webcomics. Einer meiner Favoriten ist Cyanide and Happiness (oder auch explosm). Leider bin ich erst vor Kurzem auf diese Seite gestoßen und daher habe ich viele Comics nicht gelesen. Wenn ich zwischendurch mal extreme Langeweile habe (Nein, das kommt nicht vor. Ich will mich nur vorm Lernen drücken.), möchte ich dies nun nachholen. Leider hat die Seite manchmal eine recht lange Ladezeit und sooo viel Zeit habe ich ja nun auch nicht 🙂

Daher habe ich mir mal schnell ein kleines Download-Script für die PowerShell gebastelt, das die Comics für mich herunterlädt, sodass ich sie später lesen kann. Das ganze funktioniert mit Regular Expressions, da bei explosm die Comics (also die tatsächlichen Bilddateien) nicht etwa durchnummeriert sind, sondern frei vergebene Namen haben und in verschiedenen Ordnern liegen.

Das folgende Script öffnet nach Übergabe von 2 Parametern (Start- und Endnummer) die entsprechenden Comics von explosm und versucht, die URL der Bilddatei zu ermitteln. Sollte das nicht klappen, kann es sein, dass es das Comic mit der Nummer nicht gibt (nicht schlimm), oder dass die URL nicht ausgelesen werden konnte (dann muss der RegEx angepasst werden). Bei letzterem Fall wird die Nummer in die Datei errors.txt gespeichert. Ansonsten lädt das Script die Bilddatei herunter und speichert sie mit der Nummer zu Beginn des Dateinamens ab.

explosmdownloadscript.jpg

if ($args.Count -ne 2) { Write-Host "Start and end number needed" -foregroundcolor "red"; exit; } # $start = $args[0]; $end = $args[1]; # $path = Split-Path -Parent $MyInvocation.MyCommand.Path; $pattern = [regex] 'img alt="Cyanide and Happiness, a daily webcomic" src="(?<url>http:\/\/www\.explosm\.net\/db\/files\/(Comics\/)?(?<author>[a-zA-Z0-9]*\/)?(?<file>[\(\) a-zA-Z0-9_-]*\.[a-zA-Z]{3,4}))"'; $errPattern = [regex] "WAHT YOU DOEN"; $clnt = New-Object System.Net.WebClient # for ($i = $start; $i -le $end; $i++) { Write-Host "Comic No" $i.ToString("0000"); $url = "http://www.explosm.net/comics/" + $i + "/"; $html = Get-Url $url; $m = $pattern.Match($html); if ($m.Success) { $pic = $m.Groups['url'].Value; $filename = Join-Path $path ($i.ToString("0000") + "_" + $m.Groups['file'].Value); Write-Host " Saving" $i.ToString("0000") -foregroundcolor "green"; $clnt.DownloadFile($pic, $filename); } else { $m = $errPattern.Match($html); if ($m.Success) { Write-Host " does not exist" -foregroundcolor "yellow"; } else { Write-Host " error" -foregroundcolor "red"; Add-Content "errors.txt" $i; } } }

Meine 15 Must-Have-Tools für Windows-Nutzer

Ich bereite gerade die Neuinstallation von Windows auf meinem Notebook vor (die Erstinstallation war vor 2 Jahren) und um mal eine Übersicht über die verschiedenen nützlichen Tools zu bekommen, die ich tagtäglich so nutze, habe ich mir in meinem Wiki eine Liste aller Programme angelegt, die ich bei einer Windows-Installation gleich mitinstalliere.

Meine Top 15 sind die folgenden Progamme (in dieser Reihenfolge). Natürlich sind alle kostenlos bzw. teilweise sogar Open Source.

  1. ZoneAlarm
    Das erste Programm, das nach Windows installiert wird, sollte eine Firewall sein 🙂 ZoneAlarm setze ich nun schon seit vielen Jahren ein und ich habe bislang auch keine Ambitionen sie abzulösen.
  2. AntiVir
    Als zweites ist wohl ein Virenscanner ratsam. AntiVir hat sehr schnelle Updatezeiten was die Virendefinitionen angeht und schneidet in Vergleichstest meist sehr gut ab.
  3. 7Zip
    Irgendein Entpacker muss ja sein. Früher habe ich mal FilZip verwendet, doch inzwischen bin ich bei 7Zip gelandet.
  4. PSPad
    DAS Schweizer Taschenmesser für mich! Gibt es etwas, das dieser Texteditor nicht kann bzw. das nicht über Plugins nachgerüstet werden kann?
  5. Firefox
    Muss ich dazu wirklich was sagen? Ok: WebDeveloper Toolbar, DOM Inspector, MouseGestures, FireBug, HTML Validator etc. Mehr gibt es hier: 10 reasons&questions why my firefox is so much better than your IE
  6. Thunderbird
    Analog zu Punkt 5 😉
  7. VLC Media Player
    Der beste Ersatz für den Media Player. Spielt DVDs, Streaming-Videos etc. und kann letztere sogar abspeichern.
  8. Foxit Reader
    DIE Alternative zum Adobe Reader. Einmal installiert, will man nie wieder zurück zum schwerfälligen, Feature-armen großen Bruder.
  9. TortoiseSVN
    Subversion ist der de facto Standard für die Versionsverwaltung und TortoiseSVN der beste Windows-Client. Integriert sich ins Windows-Kontextmenü. Einfacher geht’s nicht.
  10. PuTTy
    Auch Windows-Nutzer wie ich benötigen ab und an mal Zugriff auf Linux-Server. PuTTy ist mein Mittel der Wahl, wenn es um SSH-Verbindungen geht
  11. FileZilla
    Auch ein FTP-Programm sollte nicht fehlen. FileZilla hat viele Funktionen und wird ständig weiterentwickelt (wenn man mal von den häufigen Updates in letzter Zeit ausgeht ;-))
  12. GNU Utilities und gVim
    Da ich insgeheim eigentlich Linux-Liebhaber bin, installiere ich mir mit den GNU Utilities und gVim gleich die wichtigsten Linux-Befehle unter Windows. Wie schmerzlich vermisse ich sonst tail, grep, less und Konsorten (auch wenn die PowerShell schon ein Schritt in die richtige Richtung ist).
  13. Azureus
    Ein schicker und funktionsreicher BitTorrent-Client gehört zur Standardausrüstung eines Windows-PCs. Gerade wenn man häufiger mal Debian-Images und Filme downloaden muss 😉
  14. Sysinternals Suite
    Wer diese Tools, nicht installiert ist selbst schuld! So ziemlich alle wichtigen Sysinternals-Tools sind hier vereint. Seien es handle, die pstools oder autoruns.
  15. PDFCreator
    Zuletzt noch das Werkzeug meiner Wahl zum Erstellen von PDF-Dateien. Früher habe ich mal FreePDF verwendet, aber der PDFCreator ist ein wenig ausgereifter.

Hat noch jemand Ergänzungen? Die Liste im Wiki ist noch ein wenig länger, aber ich wollte es nicht übertreiben 😉

Lost Season 4

Bald ist es soweit: Lost Staffel 4 kommt!

Passend dazu gibt es eine lustige Zusammenfassung des bisherigen Geschehens. Die Länge des Videos ist 8:15 Minuten. Na, klingelt’s? 😉 Lost in 8:15 Minuten. Und einen Trailer zur neuen Staffel gibt es auch schon seit Längerem: Lost. Season 4. Trailer. Extra lang.

Heroes geht auch bald (Februar) weiter nach der mit 11 Folgen mehr als kurzen zweiten Staffel. Einen Trailer gibt es hier: Heroes – Villains Trailer

Und schon ab Montag kommen endlich die neuen Folgen von Prison Break! Ist ja auch ein Skandal, dass die nach nur 8 Folgen der dritten Staffel eine Pause gemacht haben. Tja, der Autorenstreik wirkt sich auch auf mein Leben aus 😉

Wie gut, dass ich in der Zwischenzeit mit Columbo eine gute Alternative gefunden habe. Die Originalstimme von Peter Falk gefällt mir inzwischen richtig gut und die Serie ist einfach zeitlos… Ich habe zwar sehr viele Folgen bereits auf deutsch gesehen (Wann kamen die nochmal? Dienstags abends auf RTL?), aber bei den 12 Staffeln sind doch noch einige Folgen dabei, die ich nicht kannte (z.B. die komplette dritte Staffel und viele Folgen der zweiten und fünften Staffel). Kaum zu glauben, dass die Filme teilweise schon über 35 Jahre alt sind. Und wie man hier lesen kann, soll 2008 Columbos 70ster und letzter Fall veröffentlicht werden: Inspektor Columbo ist 80.

PowerShell: Zugriff auf MySQL-Datenbank

Mit der PowerShell auf eine MySQL-Datenbank zuzugreifen ist eigentlich recht einfach, da vorhandene .NET-Klassen in der PowerShell verwendet werden können. Man benötigt nur die entsprechenden Klassen für den MySQL-Zugriff. Das ist in meinem Fall der MySQL Connector/Net 5.1. Die früheren Versionen funktionieren mit meiner Datenbank nicht (MySQL-Version 5.0):

Error connecting to the server: Unable to connect to any of the specified MySQL hosts.

Der Download ist zwar eine MSI-Datei, man benötigt aber lediglich die MySql.Data.dll aus dem Verzeichnis Binaries\.NET 2.0, nachdem die Installation abgeschlossen ist. Diese muss im selben Verzeichnis liegen, wie das folgende Script. Alternativ kann man auch in Zeile 5 einen konstanten Pfad angeben.

# the connection string used to connect to the database $connString = "Server=localhost;Uid=benutzer;Pwd=password"; # # get the script's execution path $myPath = Split-Path -Parent $MyInvocation.MyCommand.Path; # # load MySQL driver and query database [void][system.reflection.Assembly]::LoadFrom($myPath + "\MySQL.Data.dll"); $conn = New-Object MySql.Data.MySqlClient.MySqlConnection; $conn.ConnectionString = $connString; $conn.Open(); $command = New-Object MySql.Data.MySqlClient.MySqlCommand; $command.Connection = $conn; $command.CommandText = "SELECT * FROM table"; $reader = $command.ExecuteReader(); while($reader.Read()) { write-host $reader.GetString(0); }

Serverumzug (fast) abgeschlossen

Ich bin jetzt mit fast allen Websites (inkl. diesem Blog) auf meinen neuen Server (stefan-macke.com) umgezogen und muss sagen, dass die Performance deutlich zugelegt hat. Na gut, vorher hatte ich 256MB Arbeitsspeicher und nun 2GB. Da muss sich ja was tun 🙂

Jetzt fehlt nur noch die Übertragung meiner übrigen Domains auf Hetzner und dann ist alles abgeschlossen. Interessant wird noch die Übertragung des Mailservers. Obwohl auf dem neuen Server schon Postfix läuft, habe ich durch die Verzögerung aufgrund des KK-Vorgangs sicherlich einige Tage Ausfall, bis mein neuer Server als MX im DNS eingetragen ist 🙁

WordPress hat beim Übertragen gleich mal ein wenig gezickt, da es auf einmal nur noch englische Datumsangaben ausgab und auch das Backend in Englisch war. Nicht, dass mich das stören würde, aber es sieht doch unschön aus, wenn alles andere in Deutsch ist… Die Lösung für das Problem fand ich allerdings recht schnell hier: WordPress – keine deutsche Sprache mehr?. Und jetzt läuft alles wunderbar.

Mein neuer Rootserver

Zu Weihnachten muss man sich auch mal was gönnen, dachte ich mir. Und so habe ich mir vorletzte Woche einen Rootserver DS3000 von Hetzner bestellt. Bereits am folgenden Tag war der Server, am selben sogar schon der Zugang zum Webinterface für die Domainverwaltung freigeschaltet.

Nicht, dass ich mit meinem bisherigen vServer unzufrieden wäre, aber in letzter Zeit häuften sich die Ausfälle aufgrund von Speichermangel und die Reaktionszeiten waren auch nicht mehr ganz zufriedenstellend. Naja, die Ansprüche wachsen halt 😉

Meine Domain stefan-macke.com ist die erste, die bereits umgezogen ist. Dort werde ich in Kürze dieses Blog und alle übrigen Seiten neu aufsetzen. Es kann also in den kommenden Tagen ab und an zu Ausfällen dieser Seite kommen. Bis alle .de-Domains (inkl. dieser) endgültig übertragen sind, kann es aber noch ein Weilchen dauern.

Offline-Version einer (kleinen) Website erstellen

Um eine kleine Intranetanwendung auf CD bereitstellen zu können, habe ich ein kleines Shell-Script geschrieben, das mir die Webseiten als Offline-Version speichert. Die Anwendung ist in PHP geschrieben und gibt lediglich ein paar Inhalte als HTML aus, erwartet also keine Benutzereingaben usw.

Als erstes habe ich PHP so konfiguriert, dass auch HTML-Dateien geparst werden. Dadurch benötige ich nicht php als Dateiendung und kann somit Links usw. auf die HTML-Seiten setzen, da PHP-Dateien offline nicht funktionieren würden. Dazu habe ich einfach die folgende Zeile in die entsprechende VirtualHost-Datei des Apache-Servers eingetragen:

AddType application/x-httpd-php .php .html

Das Script ist nun eigentlich recht einfach. Ich definiere die Seiten, die es speichern soll, und lade diese mit lynx herunter. Danach lasse ich direkt tidy drüberlaufen, um den Quelltext zu formatieren. Fertig 🙂 Mein Script enthält dann noch ein paar simple cp-Befehle um einige benötigte Dateien und Ordner zu kopieren und (da diese mit Subversion versioniert werden) alle .svn-Ordner zu entfernen.

#!/bin/bash # HTMLDATEIEN="Seite1 Seite2 Seite3" PDFORDNER="Ordner1 Ordner2 Ordner3" # hier liegen die PHP-Dateien auf dem Webserver WEBPATH="/srv/www/offlinecd/" # hier wird die Offline-Version erzeugt CDPATH=${WEBPATH}"CD/" # hier liegen die zu kopierenden Ordner PDFPATH=${WEBPATH}"PDF/" # das ist die URL der Website URL="http://ganz.tolle.anwendung.de/" # tidy-Befehl, der über die HTML-Quellen läuft TIDYCMD="/usr/bin/tidy -i --quiet true --tidy-mark true -wrap 0 -raw --output-xhtml true --indent auto --indent-spaces 4 --break-before-br false --vertical-space no --char-encoding utf8 --fix-uri no" # if [ -e ${CDPATH} ] then echo "Verzeichnis wird geloescht: " $CDPATH rm ${CDPATH} -R fi if [ ! -e ${CDPATH} ] then echo "Verzeichnis wird angelegt: " $CDPATH mkdir -p ${CDPATH} fi # for HTMLDATEI in $HTMLDATEIEN do echo "Speichere HTML-Datei: " $HTMLDATEI lynx -source ${URL}${HTMLDATEI}".html" | ${TIDYCMD} &gt; ${CDPATH}${HTMLDATEI}".html" done # for ORDNER in $PDFORDNER do echo "Kopiere Ordner: " $ORDNER cp ${PDFPATH}${ORDNER} ${CDPATH} -R done # cp ${WEBPATH}*.gif ${CDPATH} cp ${WEBPATH}*.jpg ${CDPATH} echo "Kopiere CSS-Dateien..." cp ${WEBPATH}*.css ${CDPATH} # echo "Entferne .svn-Ordner..." find ${CDPATH} -name .svn -print0 | xargs -0 rm -R

VMWare-Server verlangsamt Windows-Anmeldung

In letzter Zeit dauerte es fast 5 Minuten, bis mein Windows XP hochgefahren war. Ich habe mich gestern mal auf die Suche nach der Ursache gemacht und dazu die üblichen Verdächtigen näher beleuchtet: %SYSTEMROOT%\Debug\UserMode\userenv.log und %SYSTEMROOT%\security\logs\winlogon.log (ich habe zunächst auf Probleme mit den Gruppenrichtlinien getippt). Dazu habe ich zunächst einmal das umfangreiche Logging aktiviert, wie hier beschrieben: Behandlung von Gruppenrichtlinienproblemen mithilfe von Protokolldateien.

Wie sich nach kurzer Analyse der userenv.log herausstellte, waren jedoch nicht die Gruppenrichtlinien schuld, sondern der VMWare-Server, den ich seit kurzem installiert habe. Die folgenden Zeilen der Logdatei zeigen, wie lange der Aufruf des VMWare-Servers dauerte:

USERENV(660.668) 14:41:49:000 LibMain: Process Name:  C:\Programme\VMware\VMware Server\vmware-authd.exe
USERENV(3e4.3e8) 14:42:37:229 LibMain: Process Name:  C:\WINDOWS\system32\wuauclt.exe

Fast eine Minute! Und es gab noch mehr Aufrufe des Servers. Ich konnte das Problem beheben, indem ich die virtuellen Netzwerkkarten des VMWare-Servers deaktivierte. Ich habe meine 2-3 VMs eh als “bridged” laufen und brauche keine virtuellen Interfaces. Die Einstellungen erreicht man unter Host → Virtual Network Settings.

Deaktivieren der virtuellen Netzwerkkarten des VMWare-Servers

Installation von Trac 0.10.4 unter OpenSUSE 10.1

Wie bereits angekündigt folgt nun die Beschreibung der Installation von Trac 0.10.4 unter OpenSUSE 10.1. Es hat gute zwei Tage gedauert, das Teil ans Laufen zu bekommen, aber nun funktioniert alles wunderbar (inkl. LDAP-Authentifizierung). Version 0.11 habe ich aber leider nicht installieren können, was mehrere Gründe hat (z.B. fehlende 64Bit-Pakete etc.).

Python und mod_python installieren

Zunächst einmal galt es Python und das Apache-Modul mod_python zu installieren, da ich Trac als VirtualHost in meiner vorhandenen Apache-Installation laufen lassen möchte, und nicht als eigenständigen Server. Nach einigem Hin und Her habe ich mich entschieden, die Pakete selbst zu kompilieren (alle Versuche mit vorkompilierten Paketen schlugen aus den unterschiedlichsten Gründen fehl). Also zunächst mal die Quellen besorgen: Python Sources (ich verwende Version 2.5.1), mod_python Sources (ich verwende Version 3.3.1).
Nach einigen Problem beim Kompilieren bin ich auf diesen sehr guten Artikel zum Thema gestoßen: Compiling mod_python on RHEL 64 bit. Dort ist z.B. die Behebung des Fehlers

/usr/local/lib/python2.5/config/libpython2.5.a: could not read symbols: Bad value
collect2: ld returned 1 exit status

beschrieben. Im Weiteren bin ich der dortigen Anleitung gefolgt und alles hat wie beschrieben funktioniert.

ClearSilver installieren

Als nächstes habe ich ClearSilver installiert. Auch dieses Paket habe ich manuell kompiliert: ClearSilver Sources (ich verwende Version 0.10.5).

Der configure-Befehl dazu sieht so aus:
./configure --with-python=/usr/local/bin/python --prefix=/opt/clearsilver-0.10.5 --disable-ruby --disable-java --disable-perl --disable-apache --disable-csharp --disable-compression
Das --disable-compression behebt den später beim Aufruf von Trac aufgetretenen Fehler: neo_cgi.so: undefined symbol: deflate

SQLite und PySQLite installieren

Auch SQLite und PySQLite habe ich selbst kompiliert. Die Sourcen gibt es hier: SQLite Sources (ich verwende Version 3.5.3), PySQLite Sources (ich verwende Version xxx).

Der configure-Befehl für SQLite sieht (einer Empfehlung von der Trac-Seite zufolge) so aus:
./configure --enable-threadsafe --prefix=/opt/sqlite
Die Lösung des dabei aufgetretenen Fehlers (sqlite3.h: No such file or directory) kann man hier nachlesen: installing “pysqlite” (include_dirs in der setup.cfg anpassen).

Für die Installation von PySQLite gibt es eine gute Anleitung auf der eigenen Seite: Installationsanleitung PySQLite. Die habe ich Schritt für Schritt durchgearbeitet und letztlich hat alles geklappt. Der einzige Fehler war der folgende:

ImportError: /usr/local/lib/python2.5/site-packages/pysqlite2/_sqlite.so: undefined symbol: sqlite3_enable_shared_cache

Dieser ließ sich jedoch wie hier beschrieben beheben: [pysqlite] Getting Error: undefined symbol: sqlite3_enable_shared_cache
Ich habe den entsprechenden Inlcude-Path (/opt/sqlite-3.5.3/lib) in die /etc/ld.so.conf eingetragen und es läuft.

Subversion installieren

Auch Subversion habe ich selbst kompiliert. Die Sourcen gibt es hier: Subversion Sources (ich verwende Version 1.4.5). Wichtig ist, auch das Paket subversion-deps-1.4.5.tar.bz2 herunterzuladen und in das Verzeichnis der Subversion-Quellen zu entpacken, da sonst einige Abhängigkeiten nicht aufgelöst werden können (Fehler: subversion cannot find -lldap).
Ein weiterer Fehler war der folgende:

libneon.a: could not read symbols: Bad value

Dieser ließ sich jedoch leicht wie hier beschrieben beheben: make error: libneon.a: could not read symbols: Bad value (neon separat kompilieren).

SWIG 1.3.27 installieren

Zusätzlich zu Subversion werden noch die Python-Bindings SWIG benötigt. Die Trac-Website rät zu Version 1.3.27 (mit höheren habe ich es daher erst gar nicht probiert): SWIG Sources.
Nachdem SWIG kompiliert wurde, muss noch im Subversion-Quellenverzeichnis ein make swig-py; make install-swig-py ausgeführt werden (wie auf der Trac-Seite beschrieben).

Trac installieren

Die Installation von Trac verlief vergleichsweise einfach: Die Sourcen downloaden (Trac Sources) und mittels python ./setup.py install installieren (wie auf der Trac-Seite beschrieben).
Beim Aufruf von trac-admin /srv/www/trac initenv kam dann allerdings die folgende Fehlermeldung:

Failed to create environment. database parameter must be string or APSW Connection object

Dies ist wohl ein Bug von PySQLite, der sich durch folgenden Patch beheben ließ: PySqlite 2.4.0 doesn’t accept an unicode object for the connection string anymore.
Nachdem ich die 4 Zeilen im Quelltext geändert hatte, konnte ich Trac installieren und es läuft (bisher) ohne Probleme.

LDAP-Authentifizierung

Die LDAP-Authentifizierung der Trac-Benutzer habe ich anhand der Anleitung auf der Trac-Seite (Configuring Authentication) unter Zuhilfenahme dieser wirklich guten Beschreibung umgesetzt: Apache and Subversion authentication with Microsoft Active Directory

Syntax-Highlighting

Anhand dieser Anleitung habe ich Trac dann abschließend noch das farbige Auszeichnen von C#-Quelltexten beigebracht: Csharp.st. Und die Datei habe ich dann gleich als Vorlage für eine Syntax-Highlighting-Definition für Natural-Sourcecode verwendet, die im Folgenden dargestellt ist. Damit kann man nun mittels enscript Natural-Quellen auszeichnen.
/* * States definitions file for Natural. * Author: Stefan Macke (me@this-domain) */ state natural extends HighlightEntry { /* Comments. */ /\/\*/ { comment_face (true); language_print ($0); call(eat_one_line); comment_face (false); } /^\*/ { comment_face (true); language_print ($0); call (eat_one_line); comment_face (false); } /* Escaped strings */ /\@\"[^"]+\"/ { string_face(true); language_print($0); string_face(false); } /* String constants. */ /\"/ { string_face (true); language_print ($0); call (c_string); string_face (false); } /'[^']+'/ { string_face (true); language_print ($0); string_face (false); } /* Character constants. */ /'.'|'\\\\.'/ { string_face (true); language_print ($0); string_face (false); } /* Keywords */ /\b(REDEFINE|DATA|USING|END-DEFINE|INCLUDE|END-READ|END-FIND|END-SUBROUTINE|END-IF|IF|DEFINE|LOCAL|END-WORK|WORK FILE|CLOSE WORK|BREAK|CONTINUE|END|CONTROL|FOR|CALLNAT|PERFORM|END-DECIDE|DECIDE|PARAMETER|GLOBAL|TRUE|FALSE|LT|GT|EQ|LE|GE|NE|SUBROUTINE|BY|STARTING FROM|WITH|TOP|BOTTOM|TO|FROM|(AD=MI)|(AD=IO)|OR|AND|(SV15)|(0)|LS|IN|FULL|GIVING INDEX|REJECT|KEY|ALL|ON|FIRST|VALUE|OF|NONE|IGNORE|READ|FIND|ESCAPE|SET|WRITE|INPUT|ADD|SUBTRACT|DISPLAY|FORMAT|RESET|ASSIGN|EXAMINE|COMPUTE|VAL|BACKOUT|TRANSACTION|STORE|UPDATE|DELETE|WORK|VARIABLE|COMPRESS|MOVE|EDITED|INTO|LEAVING|NO|VIEW)\b/ { keyword_face (true); language_print ($0); keyword_face (false); } } /* Local variables: mode: c End: */