Tips and Tricks

vim en character encodings

by hjt 04.08.2011

Moderne versies van de vim editor kunnen omgaan met verschillende
character encodings. Als de instellingen verkeerd staan kan dat
onaangename verrassingen leveren.

Onlangs kreeg ik te maken met een werkplek/account waarop de default
vim-settings waren…

Lees het volledige artikel →

Rsync en performance

by jc 04.05.2010

Het Probleem

Ik was laatst een nieuwe disk aan het in-gebruik-nemen, waarbij ik
veel grote files moest kopieren van de oude (kleinere) disk. Dat
soort dingen doe ik normaliter met rsync (daarvan zitten de opties in
mijn vingers). Maar nu zag ik dat …

Lees het volledige artikel →

Nabedrukt briefpapier

by gait 23.03.2010

Bij AT Computing zijn we overstapt van compleet voorbedrukt briefpapier
naar papier met slechts het logo voorbedrukt. Daarvoor gebruiken we
een OpenOffice.org-sjabloon met macro’s: Voor een nieuw document kun je,
uitgaande van zo’n sjabloon, kiezen ui…

Lees het volledige artikel →

Vergrotingssoftware

by hjt 05.01.2010

Onlangs kregen we een verzoek of een slechtziende persoon aan een
van onze cursussen zou kunnen deelnemen. We hebben in het verleden
vaker slechtziende en blinde cursisten gehad, en met wat extra
moeite komen zij en wij daar in het algemeen erg tevreden uit.

De docent moet zijn woorden, zijn gebaren, en het schrijven op het
bord zorgvuldig op elkaar afstemmen. Dat vereist wat oefening en
discipline. Ook de uit te reiken cursusdocumentatie moet aangepast
worden afgedrukt, en/of als PDF beschikbaar worden gesteld.
Tenslotte moet de werkplek op maat worden ingericht.
Over die werkplek gaat de rest van dit verhaaltje.

Deze cursist liet ons weten dat hij graag gebruik zou maken van
vergrotingssoftware vergelijkbaar met het produkt ZoomText van
ai squared
onder Windows.
Letters van 2 cm hoogte zouden ideaal zijn.

schermvergroting

Bij zulke grote letters wil je ook een groot scherm.
We hebben bij AT een werkplek met een
Apple Cinema HD 30 beeldscherm, 40×64 cm, 2560×1600 pixels.
Daar hoort natuurlijk een flinke grafische kaart bij: Nvidia GeForce 9600GT.
De betreffende computer is ingericht met een recente Ubuntu Linux.
Het is een uiterst comfortabele werkplek!

Bij Ubuntu is “gnome-terminal” het gebruikelijke shell-venster.
Met de maximale “zoom-in” levert dat op het genoemde beeldscherm
7 mm hoge (m-height) characters. Dat was dus nog niet genoeg.
En grafische programma’s gebruiken geen gnome-terminal, maar moeten
wel mee worden vergroot.

In eerste instantie denk je aan het commando “xmag”, maar dat kan alleen
statische delen van het scherm vergroten, en is niet echt meer van deze tijd.

In onze
Ubuntu Certified Professional cursus komt onder andere
“Accessibility” (kort) aan bod. Daaruit wisten we dat de compiz
window manager ook zoom-support kan leveren. Daarvoor moet je wel
het pakket “simple-ccsm” apart downloaden en installeren. Met de
Ubuntu “apt-get” is dat in een paar tellen gebeurd.

Instellen gaat vervolgens via het Ubuntu-menu: System -> Preferences ->
Appearance, en daar selecteer je Visual Effects -> Custom

Daarna kun je de combinatie van scrollwiel en “Special”-toets
(dat is dan de Windows-toets, zie foto van het keyboard) gebruiken
om in- en uit te zoomen.
Windows-key
Bewegen van de muis geeft “panning”: het vergrootglas
over de ondergrond bewegen. Het mooie hieraan is dat het hele
scherm wordt vergroot. Op de andere foto ziet u een stukje van
onze website, gefotografeerd op het betreffende grote beeldscherm,
en een telefoontoestel erbij als referentie.

Meer informatie is te vinden op:
http://www.howtoforge.com/enabling-compiz-fusion-on-an-ubuntu-9.10-desktop-nvidia-geforce-fx-5200-p2

Voor de volledigheid moet worden gezegd dat naast deze compiz-gebaseerde
faciliteiten ook het Orca-project in de gnome window manager
uitgebreide faciliteiten voor slechtziende en blinde gebruikers bevat.
Een projectbeschrijving vindt u hier
en op de website van Willi Walker, de voortrekker van dit project.

Het is qua rekentijd allemaal niet gratis, maar je krijgt wel wat moois.
De cursist “voelde zich met deze werkplek verwend”.

Lees het volledige artikel →

Find voor gevorderden

by hjt 28.05.2009

Een discussie onder collega’s over het find-commando herinnerde me
aan een worsteling die ik een tijdje geleden met dat commando had.
Het is een van de meest krachtige commando’s uit het repertoire,
maar — zoals oude manual pagina’s dat letterlijk zeiden –
the syntax is painful“.

find directory criteria actie(s)

Een krachtige faciliteit, maar te moeilijk geoordeeld voor onze cursussen,
is de -prune. Daarmee kun je stukken van de fileboom “snoeien”,
d.w.z. overslaan in de zoek-operatie. Om die -prune te begrijpen moet je
weten dat niet alleen de zoek-criteria een booleaanse expressie vormen,
maar dat de acties formeel ook deel van die expressie uitmaken.
Dat laatste is ook een detail dat we in onze cursussen weglaten,
uit angst dat de cursist door de bomen het bos niet meer ziet.
En bij Booleaanse expressies hoort lazy evaluation:
als een uitkomst al vaststaat voordat de laatste gedeelten
van de expressie aan de beurt zijn, dan komen ze niet meer
aan de beurt.

De manual-page zegt:

-prune True; if the file is a directory, do not descend into it.

Dat woordje “True” bevat de crux, maar je leest er zo gauw overheen.
Prune is een actie, maar heeft dus ook een booleaanse waarde, zoals
alle acties dat hebben. Dat betekent dat in expressies zoals

...  -prune -o \( ......... \)

het gedeelte rechts van de -o (or) niet meer hoeft te worden
uitgerekend als door de true van -prune, in combinatie met
wat ervoor staat, de einduitkomst true al vast zou liggen.

Kijk eens naar dit voorbeeld:

find . -name 'myjunk' -a -prune -o \( -name 'pietje.puk' -print \)

Merk op dat ik tussen -name myjunk en -prune de booleaanse
-a (and) expliciet heb opgeschreven. Dat heb ik gedaan om
nadrukkelijk op die ‘and’ te wijzen. Je mag -a weglaten.

Als we op onze tocht door de fileboom de naam ‘myjunk’ tegenkomen
dan scoort -name myjunk true. Als het ook nog een directorynaam is,
dan wordt die geskipt. De combinatie -name myjunk -a -prune
is nog steeds true. De -o (or) die daarna komt kan daar niets meer
aan veranderen, en wordt dus helemaal overgeslagen.

Echter, komen we op onze tocht door de fileboom een andere naam
tegen, dan is -name myjunk false, dus -name myjunk -a -prune
is ook false. De -o (or) die daarna komt levert eigenlijk een
compleet nieuwe kans om alsnog een true te bereiken. Dus het is nu
alsof dat hele stuk -name myjunk -a -prune buiten spel wordt
gezet, en het stuk tussen de haken na de -o als een min of meer
zelfstandige find alsnog aan het werk wordt gezet.

Resultaat: we zoeken de naam pietje.puk, maar in/onder de subdirectory
myjunk kijken we niet.

Zo’n beslissing om bepaalde (veel) details tijdens een cursus niet
te behandelen is onvermijdelijk, maar kan tot gewetensnood leiden.
Als docent denk je dan altijd ..”maar als iemand eens een commando
zus-en-zo opbouwt, dan wordt hij verrast omdat we een detail net
iets te eenvoudig hebben uitgelegd….” Of, nog erger: “stel dat
dat detail nu net wel in een van de vragen bij een LPIC-examen zit”.

Het volgende voorbeeld, weer gebaseerd op de booleaanse waarde
van een actie-component bij find, illustreert dat.

We hanteren in onze lessen de volgende systematiek bij de
behandeling:

  1. de algemene vorm van het commando is:
    find directory criteria actie(s)
  2. meerdere directories achter elkaar zijn toegestaan
  3. we zeggen dat -print de default actie is
  4. bij de booleaanse operaties met criteria vertellen we
    o.a. over: crit1 -o crit2

Maar kijk nu eens hier:

mkdir subdir; cd subdir   # maak lege subdir, ga daar naartoe
> fa; > fb                # maak in die (nu huidige) dir twee files

vb. A)  find  .     -name 'fa' -o -name 'fb'
vb. B)  find  .     -name 'fa' -o -name 'fb'    -print
vb. C)  find  .  \( -name 'fa' -o -name 'fb' \) -print

Volgens ons les-verhaal zouden A) en B) hetzelfde resultaat moeten
leveren, want een default-component mag je meegeven of weglaten
zonder verschil te maken.
Maar A) en B) geven verschillend resultaat!

De adder onder het gras is alweer dat acties ook meetellen in de
Booleaanse expressie. En omdat twee componenten zonder Booleaanse
operatie ertussen impliciet een -a (and) ertussen krijgen, moet
je B) als volgt lezen:

vb. B)  find . -name 'fa' -o -name 'fb' -a -print

en vanwege de Booleaanse precedentieregels betekent dat weer:

vb. B)  find . -name 'fa' -o \( -name 'fb' -a -print \)

Dat verklaart waarom A) en B) verschillend resultaat geven.
Voorbeelden A) en C) geven wel hetzelfde resultaat.

De moraal van het verhaal: bij een -o kun je het beste maar
altijd haakjes gebruiken. Dus nooit B) schrijven, maar altijd C).

Een ander nuttig criterium is -depth: behandel eerst
files (en subdirs) die in een directory staan, alvorens de
directory zelf te behandelen. Met andere woorden: ga eerst zo snel
mogelijk de diepte in, en schenk pas op de terugweg omhoog aandacht
aan de tussen-niveaus waar je langs kwam. Ik heb zelf de vaste gewoonte
om die -depth altijd mee te geven, want in combinatie
met de commando’s tar en cpio kun je later timestamps van de
oorspronkelijke fileboom iets nauwkeuriger reconstrueren.
Maar: de combinatie van -depth en -prune
bijt elkaar! Het besluit om een directory te skippen moet je nemen
voordat je erin duikt, en niet achteraf.

Lees het volledige artikel →

Find voor gevorderden

by hjt 28.05.2009

Een discussie onder collega’s over het find-commando herinnerde me
aan een worsteling die ik een tijdje geleden met dat commando had.
Het is een van de meest krachtige commando’s uit het repertoire,
maar — zoals oude manual pagina’s dat letterlijk zeiden –
the syntax is painful“.

find directory criteria actie(s)

Een krachtige faciliteit, maar te moeilijk geoordeeld voor onze cursussen,
is de -prune. Daarmee kun je stukken van de fileboom “snoeien”,
d.w.z. overslaan in de zoek-operatie. Om die -prune te begrijpen moet je
weten dat niet alleen de zoek-criteria een booleaanse expressie vormen,
maar dat de acties formeel ook deel van die expressie uitmaken.
Dat laatste is ook een detail dat we in onze cursussen weglaten,
uit angst dat de cursist door de bomen het bos niet meer ziet.
En bij Booleaanse expressies hoort lazy evaluation:
als een uitkomst al vaststaat voordat de laatste gedeelten
van de expressie aan de beurt zijn, dan komen ze niet meer
aan de beurt.

De manual-page zegt:

-prune True; if the file is a directory, do not descend into it.

Dat woordje “True” bevat de crux, maar je leest er zo gauw overheen.
Prune is een actie, maar heeft dus ook een booleaanse waarde, zoals
alle acties dat hebben. Dat betekent dat in expressies zoals

...  -prune -o \( ......... \)

het gedeelte rechts van de -o (or) niet meer hoeft te worden
uitgerekend als door de true van -prune, in combinatie met
wat ervoor staat, de einduitkomst true al vast zou liggen.

Kijk eens naar dit voorbeeld:

find . -name 'myjunk' -a -prune -o \( -name 'pietje.puk' -print \)

Merk op dat ik tussen -name myjunk en -prune de booleaanse
-a (and) expliciet heb opgeschreven. Dat heb ik gedaan om
nadrukkelijk op die ‘and’ te wijzen. Je mag -a weglaten.

Als we op onze tocht door de fileboom de naam ‘myjunk’ tegenkomen
dan scoort -name myjunk true. Als het ook nog een directorynaam is,
dan wordt die geskipt. De combinatie -name myjunk -a -prune
is nog steeds true. De -o (or) die daarna komt kan daar niets meer
aan veranderen, en wordt dus helemaal overgeslagen.

Echter, komen we op onze tocht door de fileboom een andere naam
tegen, dan is -name myjunk false, dus -name myjunk -a -prune
is ook false. De -o (or) die daarna komt levert eigenlijk een
compleet nieuwe kans om alsnog een true te bereiken. Dus het is nu
alsof dat hele stuk -name myjunk -a -prune buiten spel wordt
gezet, en het stuk tussen de haken na de -o als een min of meer
zelfstandige find alsnog aan het werk wordt gezet.

Resultaat: we zoeken de naam pietje.puk, maar in/onder de subdirectory
myjunk kijken we niet.

Zo’n beslissing om bepaalde (veel) details tijdens een cursus niet
te behandelen is onvermijdelijk, maar kan tot gewetensnood leiden.
Als docent denk je dan altijd ..”maar als iemand eens een commando
zus-en-zo opbouwt, dan wordt hij verrast omdat we een detail net
iets te eenvoudig hebben uitgelegd….” Of, nog erger: “stel dat
dat detail nu net wel in een van de vragen bij een LPIC-examen zit”.

Het volgende voorbeeld, weer gebaseerd op de booleaanse waarde
van een actie-component bij find, illustreert dat.

We hanteren in onze lessen de volgende systematiek bij de
behandeling:

  1. de algemene vorm van het commando is:
    find directory criteria actie(s)
  2. meerdere directories achter elkaar zijn toegestaan
  3. we zeggen dat -print de default actie is
  4. bij de booleaanse operaties met criteria vertellen we
    o.a. over: crit1 -o crit2

Maar kijk nu eens hier:

mkdir subdir; cd subdir   # maak lege subdir, ga daar naartoe
> fa; > fb                # maak in die (nu huidige) dir twee files

vb. A)  find  .     -name 'fa' -o -name 'fb'
vb. B)  find  .     -name 'fa' -o -name 'fb'    -print
vb. C)  find  .  \( -name 'fa' -o -name 'fb' \) -print

Volgens ons les-verhaal zouden A) en B) hetzelfde resultaat moeten
leveren, want een default-component mag je meegeven of weglaten
zonder verschil te maken.
Maar A) en B) geven verschillend resultaat!

De adder onder het gras is alweer dat acties ook meetellen in de
Booleaanse expressie. En omdat twee componenten zonder Booleaanse
operatie ertussen impliciet een -a (and) ertussen krijgen, moet
je B) als volgt lezen:

vb. B)  find . -name 'fa' -o -name 'fb' -a -print

en vanwege de Booleaanse precedentieregels betekent dat weer:

vb. B)  find . -name 'fa' -o \( -name 'fb' -a -print \)

Dat verklaart waarom A) en B) verschillend resultaat geven.
Voorbeelden A) en C) geven wel hetzelfde resultaat.

De moraal van het verhaal: bij een -o kun je het beste maar
altijd haakjes gebruiken. Dus nooit B) schrijven, maar altijd C).

Een ander nuttig criterium is -depth: behandel eerst
files (en subdirs) die in een directory staan, alvorens de
directory zelf te behandelen. Met andere woorden: ga eerst zo snel
mogelijk de diepte in, en schenk pas op de terugweg omhoog aandacht
aan de tussen-niveaus waar je langs kwam. Ik heb zelf de vaste gewoonte
om die -depth altijd mee te geven, want in combinatie
met de commando’s tar en cpio kun je later timestamps van de
oorspronkelijke fileboom iets nauwkeuriger reconstrueren.
Maar: de combinatie van -depth en -prune
bijt elkaar! Het besluit om een directory te skippen moet je nemen
voordat je erin duikt, en niet achteraf.

Lees het volledige artikel →

deeplink foto’s van een website markeren

by hjt 07.01.2009

Een bepaalde website die ik beheer bevat veel mooie foto’s.
Bezoekers worden enthousiast, en praten erover op discussiefora
waar ze lid van zijn. Soms zetten ze in hun forumbericht een link
naar een van de foto’s op “mijn” site.
Niet als clickable link, maar zodanig dat de foto rechtstreeks
ingebed in hun bericht wordt getoond.
Dan lijkt het alsof die foto van henzelf is,
terwijl hij in werkelijkheid steeds van mijn site opgeroepen wordt
zonder dat de lezer dat meteen ziet.
Deze techniek heet “deep-linking”.
Voor zover ik weet is er Nederlandse jurisprudentie
die dat zonder toestemming strafbaar acht als een copyright-violation.
Als de dader er nog een bronvermelding bij zou zetten
zou het wat mij betreft niet zo erg zijn;
de betreffende site is niet commercieel, en de foto’s zijn dat ook niet.
En de verbruikte transmissie-bandbreedte is best wel te betalen.
Maar het weglaten van bronvermelding ergerde me.
Daarom besloot ik daar zelf maar voor te gaan zorgen.

Het probleem valt uiteen in een paar stappen:

  1. hoe kan een Apache webserver zien dat vanuit een vreemde webpagina
    (d.w.z. vanuit een pagina die niet van zijn eigen site is)
    een foto/image wordt opgevraagd.
  2. hoe trigger je in zo’n geval een actie die iets anders doet
    dan braaf de gevraagde foto serveren.
  3. hoe schilder je “on the fly” een stringetje tekst in een foto.

Stappen 1 en 2 pak je aan met de “mod_rewrite” van Apache,
en stap 3 kan met PHP en z’n grafische GD-library.

In de httpd.conf van Apache controleer je dat ‘mod_rewrite’ wordt geladen
(dat is i.h.a. default) en voeg je, in de “container” van de virtual host
website, de volgende regels toe.
De nummers in de kantlijn horen er niet bij; die zijn voor de uitleg die volgt:

01 RewriteEngine on
02 RewriteLog /xxxx/logs/rewrite_log
03 RewriteLogLevel 0

04 RewriteCond %{HTTP_REFERER} !^http://www.mijnsite.nl/.* [NC]
05 RewriteRule .*\.jpe?g$|.*\.gif$|.*\.png$
                 /rewriteimage.php?URI=%{REQUEST_URI} [NC,last]

In regel 01 schakelen we het rewrite-module aan.
Dat moet expliciet want het is een setting die je nooit “erft”
uit een hoger configuratie-niveau.
Met dat module kun je binnenkomende URL’s bekijken,
en onder voorwaarden wijzigen/herschrijven.
Dat luistert nogal nauwkeurig,
dus het heeft zin om debug-logging aan te zetten (02, 03).
Regel 02 vertelt hoe de logfile moet heten.
De ‘xxxx’ in regel 02 moet in je UNIX-servermachine een absolute
padnaam vormen, en gebruiker ‘apache’ moet in die logfile kunnen schrijven.
Je kiest typisch dezelfde directory als waar ook de access-logfile
en de error-logfile staan (zoek naar AccessLog en ErrorLog regels).
Rewrite-logging is erg duur. Regel 03 zet deze logging effectief uit.
Om het tijdens het testen (tijdelijk!!) aan te zetten moet je het
level-getal opkrikken, tot maximaal 9.

Regel 04 bekijkt de ‘HTTP_REFERER’ string.
Dat is de aanvragende website; we willen weten of dat ‘onze eigen’ website is.
Zo’n regel bestaat uit 3 of 4 delen:
(deel a) keyword ‘RewriteCond’ zegt dat een voorwaarde getest moet worden.
(deel b) %{HTTP_REFERER} benoemt het slachtoffer:
de REFERER-string uit het binnengekomen HTTP-request.
Dit deel (b) van de regel kent zeer veel verschillende mogelijkheden;
de Apache-mod_rewrite documentatie is hierbij onontbeerlijk.
Deel (c) is een reguliere expressie, Perl-stijl,
die we op deel (b) willen loslaten. Dat levert dan wel of geen match op.
Maar Apache heeft als extra mogelijkheid een ! voor de expressie,
die je gebruikt als je juist niet een match zoekt. Dat doen wij dus.

Deel (d), tussen [] haken, is een optioneel aanhangel
waarin je een reeks “vlaggen” kunt zetten; ook hier weer veel mogelijkheden.
In ons geval gebruiken we vlag [NC] (regexp-match NotCase sensitive).

Dan komen we op regel 05; die moet je als één lange regel schrijven.
Deze RewriteRule is de work-horse, die het echte werk doet.
En ook zo’n RewriteRule heeft weer 3 of 4 delen.
Het keyword RewriteRule (deel a) spreekt voor zich.
Maar je moet wel weten dat een RewriteRule alleen maar dienst doet
als alle RewriteCond statements die er onmiddellijk aan vooraf gaan
een ‘true’ hebben gescoord.

Delen (b) en (c) van een RewriteRule vormen een search+replace.
(b) is een Perl-regexp die moet matchen op een deel van de gevraagde filenaam,
(c) is de replace-string.
Optioneel deel (d), tussen [], bevat weer vlaggetjes.

De reguliere-expressie in ons deel (b) ziet er wat ingewikkeld uit.
Je moet ‘m even in drie stukken knippen bij de |-tekens.
Dan krijg je drie aparte expressies; de | vormt een OR van die drie.
De eerste van de drie is .*\.jpe?g$ en betekent stukje voor stukje:
.* pakt een willekeurig (longest match) beginstuk,
daarachter \. een letterlijke punt, daarachter de letters jp,
dan betekent e? optioneel een letter e, dan een letter g,
en tenslotte betekent $ het einde van de string.
We zoeken dus naar filenaam-extensies .jpg en .jpeg
(en de vlag [NC] had je natuurlijk ook weer gezien).
De andere twee van de drie expressies zoeken vergelijkbaar
naar .gif en naar .png extensies.

Deel (c) in de regel is de replace: we vervangen de opgevraagde filenaam
door de vast gekozen filenaam /rewriteimage.php (een PHP-programma
dat we hierna bespreken; de naam hebben we zelf verzonnen)
en met een ‘?’ koppelen we daar de oorspronkelijke gevraagde
REQUEST_URI (de naam van de foto-file) als z.g. HTTP-GET-parameter aan vast.
De vlag ‘[last]‘ betekent: verder geen andere rewrite-regels meer toepassen;
eindbestemming bereikt. Default gedrag is dat de hele rewrite-matching
met de gewijzigde filenaam opnieuw begint, en dat zou met onze criteria een
oneindige loop opleveren.

Nog even wat fijnproeverij over het verschil tussen een URI (I=identifier)
en een URL (L=locator): http://www.mijnsite.nl/aap/noot/mies.jpg is een URL,
en aap/noot/mies.jpg is een URI.

We zijn een heel eind gevorderd: als vanuit een vreemde website
een .jpg of .jpeg of .gif of .png bestand van onze website wordt opgeroepen,
start Apache ons PHP-programma /rewriteimage.php en geeft als GET-parameter
daar de opgevraagde bestandsnaam aan door.
Alle data die ons PHP-programma op zijn stdout-kanaal naar buiten pompt,
gaan terechtkomen op het bordje van de browsende klant die op de gevraagde
foto staat te wachten.

Nu bekijken we dat /rewriteimage.php -programma.
Als je geen PHP spreekt kun je misschien nog de grote lijnen volgen.
Als je wel PHP spreekt kun je details over de gebruikte functies
nalezen op de PHP website (merk rechtsboven op die webpagina
de ‘search … function list’ op!).

   <?php
01   $uri = $_GET['URI'];
02   $ext = substr($uri, 1+strrpos($uri, '.'));

03   switch(strtolower($ext)) {
04   case 'jpeg': case 'jpg':
05       $inputfunction  = 'ImageCreateFromJPEG';
06       $outputfunction = 'ImageJPEG';
07       header('Content-Type: image/jpeg');
08       break;
09   case 'gif':
10       $inputfunction  = 'ImageCreateFromGIF';
11       $outputfunction = 'ImageGIF';
12       header('Content-Type: image/gif');
13       break;
14   case 'png':
15       $inputfunction  = 'ImageCreateFromPNG';
16       $outputfunction = 'ImagePNG';
17       header('Content-Type: image/png');
18       break;
19   default:
20       exit(1);
21   }

22   $handle = $inputfunction($_SERVER['DOCUMENT_ROOT'].'/'.$uri);
23   $xsize = ImageSx($handle);
24   $ysize = ImageSy($handle);

25   if (($xsize >= 190) && ($ysize >= 40)) {
26       $white = ImageColorAllocate($handle, 0xFE, 0xFE, 0xFE);
27       $black = ImageColorAllocate($handle, 0x01, 0x01, 0x01);
28       $font = 'LucidaSansDemiBold';
29       $text = 'from: www.mijnsite.nl';
30       ImageTTFText($handle, 10, 0, 2, $ysize-2, $black, $font, $text);
31       ImageTTFText($handle, 10, 0, 3, $ysize-2, $white, $font, $text);
32   }

33   $outputfunction($handle);
    ?>

Regel 01 haalt de GET-parameter met de URI-string op.
Dat is dus de naam van het foto-bestand dat door de browsende klant is opgevraagd;
via de Apache-rewrite hadden we die naam laten doorgeven.
Regel 02 peutert de extensie van die filenaam apart.
De extensie gebruiken we in regels 03 t.m. 21
om twee functienamen te vormen, afhankelijk van het type image waar het om gaat.
Ook sturen we (07,12,17) alvast de verplichte http-header naar de browser toe.

Regel 22 haalt het gevraagde foto-bestand van disk.
Merk op dat we de document-root van de website voor de URI-naam moeten plakken.
Als uw website erg ingewikkelde filenamen gebruikt,
dan is dit statement mogelijk iets te simpel.
Regels 23 en 24 kijken hoe groot de ingelezen foto is.
Regel 25 voorkomt dat we in al te kleine plaatjes
nog tekst erbij proberen te frutten.
De getallen corresponderen ongeveer met de afmeting van de tekst
die we gaan schrijven, plus een randje eromheen.
Regels 26 en 27 definiëren de kleuren wit en zwart.
We kiezen 0xFE in plaats van 0xFF, en 0×01 in plaats van 0×00 om net naast de
“echte” kleuren wit resp. zwart te gaan zitten. De reden bespreken we hieronder.
Regels 28 en 29 spreken voor zich; het genoemde font moet natuurlijk wel
in TTF-formaat op de server beschikbaar zijn voor PHP.
In regel 30 schrijven we onze tekst in zwarte letters in de foto.
Regel 31 doet dat nogmaals in witte letters, en ietsje verschoven.
Regel 33 perst het resultaat naar buiten, naar de browsende klant.
Klaar…..

Het doel is bereikt: als een foto in mijn eigen webpagina verschijnt
is hij ongemarkeerd, maar verschijnt hij deep-linked in “andermans”
pagina dan staat mijn bronvermelding in de foto geschreven.

In GIF en PNG plaatjes kan een bepaalde kleur tot “transparant” zijn verklaard,
en vaak wordt “echt” zwart (0×000000) of “echt” wit (0xFFFFFF) gekozen omdat dat
de oorspronkelijke achtergrondkleur was.
Door met onze eigen kleurdefinities daar net naast te gaan zitten (regels 26/27)
minimaliseren we de kans dat onze zwarte of witte tekstcomponent (regels 30/31)
in het plaatje transparant wordt gemaakt.

Merk op dat onze aanpak je niet beschermt tegen iemand die een kopie
van je foto op zijn eigen webserver plaatst, en ‘m vanaf daar serveert.
Dat kun je niet tegenhouden, zelfs niet met een simpel JavaScriptje
dat een klik van de rechter muisknop afvangt en een “Foei, mag niet”
pop-up displayt.
Je kunt gestolen kopieën alleen maar achteraf proberen op te sporen.
Als je zo iets nodig hebt moet je eens kijken bij
een specialist in deze zaken.

Lees het volledige artikel →

Dual boot op een Acer Aspire One

by simon 04.12.2008

Stel, je hebt net een prachtige Acer Aspire One gekocht en je denkt, dat
Linpus Linux is wel grappig, maar ik wil toch ook een echt OS gebruiken.
Als je dan ook nog ruimte genoeg hebt, omdat je een 120GB harddisk erin
hebt, waarom niet dual boot maken?

Waarschuwing: Als je dit gaat proberen met je eigen Acer Aspire One of
vergelijkbare hardware, zorg dan voor een goede werkende backup! De
restore operatie van Acer veegt je hele schijf leeg en het resizen van
de disk kan ook mislukken!!

Een beetje googlen levert wel wat nuttige informatie op over het
installeren van Ubuntu of Fedora op zo’n apparaat, maar niet
echt over dual boot. Maar ja, wat voor problemen kun je nou helemaal
tegenkomen?

Nou ja, bijvoorbeeld dat Acer standaard 119 GB voor Linpus
reserveert en 1GB voor swap. (De getallen zijn enigzins
fictief, aangezien de disk 120.000.000.000 bytes is en niet 120
GiB
.)

Poging 1

Dus start je op met je verse bootable USB sticky met bijvoorbeeld
Fedora 10 erop, maar Ubuntu, of iets anders kan ook natuurlijk, en die
heeft de optie “resize partition”, dus da’s handig (NOT!). Deze optie
sloopt namelijk gewoon een x aantal bytes van je partitie af, zonder het
filesystem erop te resizen, dus, back to the drawingboard…

Na het rescue verhaal van Acer te hebben toegepast (boot van de rescue
dvd, usb-stick van >= 2G erin, sticky aanmaken, Acer Aspire One booten
van het sticky (bij het opstarten het bootmedium kiezen met toets F12)
en dan het systeem opnieuw Linpussen) kun je vrolijk weer opnieuw
beginnen.

Poging 2

We booten opnieuw van ons Fedora sticky, zoeken een shell op, even root
worden (su -) en dan gaan we de Linpus partitie resizen (voor mij leek
8GB wel genoeg):

resize2fs -p /dev/sda1 2000000 (8GB in 4k blocks)

e2fsck -f /dev/sda1

Nu starten we de systeem installatie opnieuw en dan resizen
we de partitie sda1 tot iets wat een beetje groter is dan 8GB, bijv. 8,5GB, voor de
zekerheid. Daarna maken we partities aan voor het te installeren
systeem. (De Linpus swap heb ik verwijderd, omdat die aan het einde van
de disk stond, en een nieuwe aangemaakt
meteen na de eerste partitie)

Nu heb ik de partities:

  • sda1 (Linpus) 8,5G
  • sda2 (swap) 1,1G
  • sda3 (extended) 108G
  • sda5 (/) 12G
  • sda6 (/home) 90G

De bootloader

Bij het installeren van Fedora (of Xubuntu, waar ik in dit stadium
inmiddels mee bezig ben) kun je kiezen om grub op de root partitie te
installeren in plaats van de MBR, waar de originele Linpus bootloader
al op staat. Dit doen we en om het nu netjes te doen gaan we de oorspronkelijke Linpus bootloader
(ook grub) aanpassen zodat ook de nieuwe Linux daarbij te kiezen valt.

De boot configuratie van Linpus staat op /dev/sda1, dus die moeten we
eerst mounten;

mkdir /tmp/a1; mount /dev/sda1 /mnt/a1
cd /mnt/a1/boot/grub
vim grub.conf

NB: normaal gesproken wordt dacht ik menu.lst gebruikt, maar hier dus
niet…

hier voegen we in, tussen “hiddenmenu” en “title Linpus Linux RCD”

(X = rootpartitie van de nieuwe linux -1!):

title Nieuwe Linux
root (hd0,X)
chainloader +1

NB: als de nieuwe linux op sda5 staat wordt het root (hd0,4)

Nu kun je bij het booten kiezen voor de originele Linpus, of de
bootopties die er door de nieuwe Linux bijgemaakt zijn.

En verder:

Nu kunnen we gaan optimaliseren en spelen en als er iets stuk gaat
kunnen we altijd terugvallen op het originele OS.

Links:

Lees het volledige artikel →

Shell scripting

by miekg 12.11.2008

Als je in de UNIX shell programmeert dan gebruik je veel
externe utilities om files te manipuleren. Standaard
utilities zoals head en tail bijvoorbeeld.
Het telkens opstarten van zo’n tooltje levert
een (kleine) performance hit op. Zou het daarom niet beter
zijn om deze te vervangen door pure shell varianten?

Voor head en tail hebben we dit gedaan in de
zsh shell (homepage). Er was wel
een paar problemen die opgelost moesten worden:

  1. interpretatie van escape sequences (bv \n) in de files.
  2. lege regels die hier en daar op de proppen kwamen.

Samen met Ton Kersten kwamen we tot de volgende implementaties.

head

Het volgende zsh script implementeert head, er worden twee
opties ondersteund: -n N om het aantal regels te selecteren
en een -h voor een kleine help.

 1  #!/bin/zsh
 2  IAM="${0##*/}"
 3  typeset -i n=0
 4
 5  o_line=(-n 10)
 6  zparseopts -D -K -- n:=o_line h=o_help
 7  if [[ $? != 0 || "$o_help" != "" ]]; then
 8      echo "Usage: $IAM [-n LINES] [-h]"
 9      exit 1
10  fi
11
12  o_line=$o_line[2]
13  files=($@)
14  [[ $o_line -le 0 ]] && exit
15  [[ $#      -eq 0 ]] && files=(/dev/stdin)
16
17  IFS=''
18  for f in $files; do
19      n=0
20      while read -r line; do
21      print "${line//\\/\\\\}"
22      n=n+1
23      [[ $n -ge $o_line ]] && break
24      done < $f
25  done

De regels 1-10 zorgen voor de optie parsering en initialiseren wat
variabelen. Vanaf regel 12 wordt het interessant. De variabele $o_line
heeft in het tweede element het aantal regels dat we willen zien.
Hier pakken we dat getal.

Regel 13; de array files wordt gevuld met de overgebleven argumenten.

Regel 14; als het aantal regels dat we willen zien kleiner gelijk 0 is,
zijn we snel klaar.

Regel 15; geen files opgegeven? Dan gebruiken we standaard input.

Regel 17; zorg ervoor de er geen interpretaties plaatsvinden door
IFS (Internal Field Seperator) variabele leeg te maken.

Regel 18; start de loop die over onze files heen loopt, deze loop
eindigt op regel 25.

Regel 20; lees een regel uit de file, gebruik -r voor raw mode
(geen interpretatie).

Regel 21: als we een escape karakter tegenkomen, escape deze dan
nogmaals, zodat deze weer normaal op het scherm terecht komt als we
gaan printen.

Regels 22-23: hebben we al genoeg regels gelezen? Zo ja, ga dan verder
met de volgende file.

Regel 24: laat de while-read-loop uit de huidige file lezen.

tail

Hier de implementatie voor tail:

 1  #!/bin/zsh
 2  IAM="${0##*/}"
 3  typeset -i n=0
 4
 5  o_line=(-n 10)
 6  zparseopts -D -K -- n:=o_line h=o_help
 7  if [[ $? != 0 || "$o_help" != "" ]]; then
 8      echo "Usage: $IAM [-n LINES] [-h]"
 9      exit 1
10  fi
11
12  o_line=$o_line[2]
13  files=($@)
14  [[ $o_line -le 0 ]] && exit
15  [[ $#      -eq 0 ]] && files=(/dev/stdin)
16
17  IFS=''
18  for f in $files
19  do
20      lines=()
21      n=1
22      while read -r line
23      do
24          lines[$n]+="${line//\\/\\\\}\n"
25          n=n+1
26
27          if [[ $n -gt $o_line ]]
28          then
29              let b=$n-$o_line-1
30              lines[$b]=""
31          fi
32      done < $f
33      print "${lines%%\\n}"
34  done

Het begin van tail is bijna identiek aan die van head. De
belangrijkste loop begint hier op regel 18. Het gebruikte algoritme van tail is om
de laatste N regels te onthouden zodat die, als we het einde van de file
hebben bereikt, geprint kunnen worden.

Een tweede iets dat hier gebruikt wordt is dat een waarde in een array
in ZSH geleegd kan worden door er een lege string aan toe te wijzen
(regels 30 en 31). Zo houden we niet de hele file in memory, maar alleen
het stuk dat we willen tailen.

Regel 33 is nodig om de laatste newline in $lines er weer af te strippen.

Is het nu sneller?

Laten we een paar tests doen met een file op een Linuxsysteem,
namelijk /proc/interrupts. De scripts hierboven zijn voor
deze tests omgebouwd tot de functies: tail_zsh en head_zsh. Dit
heeft tot gevolg dat er geen fork wordt uitgevoerd.

Voor de tests gebruiken we de volgende command line:

time ( for i in $(seq 0 20000); do $prog -n 15 /proc/interrupts > /dev/null; done )

Waarbij $prog, respectievelijk head, head_zsh, tail en tail_zsh
is.

head vs head_zsh

Voor head:

0.11s user 0.01s system 0.12s elapsed

En head_zsh:

0.11s user 0.01s system 0.12s elapsed

tail vs tail_zsh

Voor tail is de timing:

0.11s user 0.02s system 0.12s elapsed

En voor tail_zsh:

0.11s user 0.01s system 0.12s elapsed

Het lijkt erop dat onze implementaties dus net zo snel zijn als de standaard
Linux tools. Waarschijnlijk is de standaard tail implementatie bij
grotere files wel veel sneller, aangezien die in een keer naar het einde van
de file kan springen. Onze tail moet eerst de gehele file
doorspitten.

Lees het volledige artikel →

Automagisch mounten van ISO-images

by jacco 31.10.2008

Met de tool AutoFS kunnen devices (partities, NFS/CIFS-shares,
ISO-images, e.d.) gemount worden wanneer een vooraf bepaalde
directory wordt benaderd. D.w.z. wanneer iemand met ls of cd iets in
die directory probeert te doen.

Dit kan eenvoudig gebruikt worden om de overvloedige voorraad aan
ISO-files met Linux-distributies te mounten wanneer het nodig is. Na een
bepaalde idle-time worden ze ook weer automatisch ge-unmount.
Onderstaand voorbeeld gaat uit van een RedHat-achtige distributie.

  • Controleer of het pakket AutoFS is geinstalleerd.
# rpm -q autofs
autofs-5.0.1-0.rc2.88
  • Is dat niet het geval, installeer hem dan.
# yum install autofs
  • Zorg er voor dat AutoFS loopt en automatisch gestart wordt.
# service autofs start
# chkconfig autofs on
  • Voeg de volgende entry toe aan /etc/auto.master:
/distro /etc/auto.distro
  • Maak de directory /distro aan.
# mkdir /distro
  • Maak het bestand /etc/auto.distro aan en geef het bijvoorbeeld de volgende inhoud:
# This is an automounter map and it has the following format
# key [ -mount-options-separated-by-comma ] location
# Details may be found in the autofs(5) manpage
centos51    -fstype=iso9660,ro,loop   :/OS/CentOS51/CentOS-5.1-i386-bin-DVD.iso
centos52    -fstype=iso9660,ro,loop   :/OS/CentOS52/CentOS-5.2-i386-bin-DVD.iso
fedora8     -fstype=iso9660,ro,loop   :/OS/Fedora_8/Fedora-8-i386-DVD.iso
fedora9     -fstype=iso9660,ro,loop   :/OS/Fedora_9/Fedora-9-i386-DVD.iso
ubuntu710   -fstype=iso9660,ro,loop   :/OS/Ubuntu/ubuntu-7.10-desktop-i386.iso
ubuntu804   -fstype=iso9660,ro,loop   :/OS/Ubuntu/ubuntu-8.04-desktop-i386.iso
  • Herlaad AutoFS (dit moet gedaan worden na elke aanpassing):
# service autofs reload
  • Als je nu naar (bijvoorbeeld) de directory /distro/centos52 gaat wordt de ISO-image /OS/CentOS52/CentOS-5.2-i386-bin-DVD.iso automatisch gemount.
# cd /distro
# ls
# cd centos52
# ls
CentOS                 RELEASE-NOTES-de          RELEASE-NOTES-es
EULA                   RELEASE-NOTES-de.html     RELEASE-NOTES-es.html
GPL                    RELEASE-NOTES-en          RELEASE-NOTES-fr
NOTES                  RELEASE-NOTES-en.html     RELEASE-NOTES-fr.html
RELEASE-NOTES-cs       RELEASE-NOTES-en_US       RELEASE-NOTES-ja
RELEASE-NOTES-cs.html  RELEASE-NOTES-en_US.html  RELEASE-NOTES-ja.html
# mount | grep cent
/OS/CentOS52/CentOS-5.2-i386-bin-DVD.iso on /distro/centos52 type iso9660 (ro,loop=/dev/loop0)

Lees het volledige artikel →