Common wget parameters

Verboseness

-nv           Be less verbose
-q            Be silent
-S            Print server response

Downloading

-c            Continue
-l level      Maximum recursive depth
-k            Convert links to local
-N            Don't download unless timestamp is newer
-nc           Don't overwrite existing files
-nd           Don't create directories
-np           Don't recurse to parent level
-p            Page requisites (images, etc)
-r            Recursive

Common curl parameters

Modify request

-H  --header                 Send header, e.g. -H "X-Authorization: Bearer 2349283493874"
-d  --data                   Send data along with request, uses POST implicitly
-X  --request method         Force method to use
-A  --user-agent             User agent
-u  --user user[:pass]       Send username and password
-e  --referer referer        Add referer
-c  --cookie-jar file        Use cookie jar file
-b  --cookie string/file     Read cookies from string or file

Behavior

-L  --location               Follow redirects
    --compressed             Request compressed response
    --ntlm                   Use NTLM authentication

Output

-I  --head                   Show header only
-s  --silent                 Silent mode
    --ssl                    Try TLS/SSL
-v  --verbose                Verbose (debug) output
-o  --output file            Output to file instead of stdout

How I configure my WAMP stack

WAMP = Windows, Apache, MySQL and PHP...

First steps.

I'm assuming you have Windows installed. Good. Shed a tear or two for the immense sadness that you have to run an *AMP stack on Windows, because now you don't have access to apt-get or yum, which are an *AMP stack's best friend. Crack a beer open, and drink to the misfortune of all programmers who develop PHP stuff on Windows. Or, as in my case, eat a Skor bar.

Now.

  • Download Apache 2.4 (at the time of this writing) from http://www.apachelounge.com/download - I suggest you get the VC11 Win32 branch, everything gets a lot easier with Win32. Skip the extra modules unless you need them.
  • Download MySQL from http://dev.mysql.com/downloads/mysql/ and install it. Doesn't matter which version. Knock yourself out. Set a good password for the root user.
  • Download PHP from http://windows.php.net/download/ - get the VC11, x86, thread-safe version. You need thread-safe because you're using Apache, and x86 because you got the Win32 build of Apache. Since you're getting them from apachelounge, they build it using a much more modern Visual C as well, so you want the VC11 version of PHP.
  • Download NetBeans from https://netbeans.org/downloads - I suggest the PHP version which is somewhat smaller and doesn't include a ton of stuff for C++ and Java.

Okay, you've got everything? You installed MySQL too? Good.

Did you verify the checksums? If you're setting up a production environment, you kind of should; but I'd rather use Linux for production stuff. You get automatic security updates if you do that. If you're on Windows, you don't; you have to keep track of new versions as they come out and update yourself. It hurts. Shed another tear for the misery of all the brave Windows developers before you, and those that will come after.

Also, install Notepad++ if you haven't, it's great. Get it from http://notepad-plus-plus.org/.

Install Apache

Now, we're going to install apache. That's a lot of fun.

  1. Since apachelounge's binaries don't come with an installer, you have to do it yourself. No big deal, it's pretty easy. Just unpack the zip file somewhere you'd like it to be, like c:\Apache24. I hate polluting the drive root, so I usually put it somewhere else, like c:\local\apache or c:\apps\apache, but that also means that I have to change all the paths in the configuration files. If you want to do that, that's fine with me.
  2. Change all the paths in the configuration file. :) That would be c:\apps\apache24\conf\httpd.conf if you put it in that folder. Everything that reads c:/apache24 must be changed to c:/apps/apache24 or wherever you put it. Yes, everywhere in the Apache config, backslashes must be turned to frontslashes, like c:/apps/apache24/htdocs.
  3. When you're looking through the httpd.conf file, make sure you find the modules list, and uncomment the following. LoadModule rewrite_module modules/mod_rewrite.so This loads the rewriting module, which is nice to have, especially if you use any PHP frameworks.

    Add index.php to the DirectoryIndex line, like this: <IfModule dir_module> DirectoryIndex index.html index.php </IfModule> And, uncomment the vhosts include config: # Virtual hosts Include conf/extra/httpd-vhosts.conf This allows us to set up virtual hosts, which is going to be way cool.
  4. Open up the conf\extra\httpd-vhosts.conf file. Get rid of all the stuff in there, either by deleting it or commenting it out for later reference.

    Because we're going to work with virtual hosts here, we need to think about what virtual hosts we want. For instance, if you have a blog you're running, why not use the name http://blog.local/ - I think that's a scheme that usually works great. We can use blog.local, www.local, wiki.local, and any other name you can think of.

    I like to put them all together in one place, like c:\apps\web\blog, c:\apps\web\wiki, etc. You can also put them in your Dropbox folder, if you're using that; or you can create a Windows junction from c:\apps\web into a relevant place in your dropbox folder - Dropbox works great with junctions. (See the Windows mklink command.)

    Anyway, whatever web root you choose, add the following to the httpd-vhosts.conf file: # Default definition <Directory "C:/apps/web"> AllowOverride FileInfo AuthConfig Limit Require ip 127.0.0.1 </Directory> That makes sure you can access the files using Apache, and only from your local machine. That's probably what you want.

    Then, for each website you're going to set up, add entries similar to these (I use two hosts here, blog.local and wiki.local, to examplify): <VirtualHost *:80> DocumentRoot "c:/apps/web/blog/public" ServerName blog.local ErrorLog "c:/apps/apache24/logs/blog-error.log" CustomLog "c:/apps/apache24/logs/blog-access.log" common </VirtualHost> <VirtualHost *:80> DocumentRoot "c:/apps/web/wiki/public" ServerName wiki.local ErrorLog "c:/apps/apache24/logs/wiki-error.log" CustomLog "c:/apps/apache24/logs/wiki-access.log" common </VirtualHost> This creates websites on your computer that you can use. The document root points to the source directory for your project, you set an individual ServerName, which is what Apache is going to match against (feel free to add ServerAlias if you want to match against more names), and you provide error and access logs for that server in the apache directory.

    I always point the DocumentRoot to a public folder under the main project directory. This means that I can keep lots of files outside of the server root, like application configuration files, class directories, etc, etc. I hate putting things into the accessible web server root that shouldn't be accessible, but your mileage may vary. Mosts PHP projects seem not to care.

    Don't forget to create the directory structure, or Apache will complain.
  5. Now, we have one thing missing. The computer doesn't know what blog.local and wiki.local are. So we have to map those to an IP address, which will be localhost.

    In the directory c:\windows\system32\drivers\etc, there is a file called hosts. We will add some entries to this file. But since it's write-protected by default, we have to change the access settings on it first. Under "properties" and "security", make sure that Users have Modify access to the file.

    Then open it in Notepad++ or Notepad or whatever you use, and add the following lines: 127.0.0.1 blog.local 127.0.0.1 wiki.local Save it, and restore the security settings so stupid malware doesn't modify it and redirect "www.google.com" to some Russian browser hijacking site.

    Test the configuration by typing c:\>ping blog.local You should now be pinging yourself at 127.0.0.1.
  6. Okay, test the setup. Go to c:\apps\apache24\bin and run c:\apps\apache24\bin>httpd -t Which tests the configuration and makes sure everything looks good. Pay attention to any error messages that appear.

    Let's install it as a service now. To do this, you usually have to be Administrator, which can be done by finding the cmd.exe program in the menus somewhere ... oh, what do they call it? ... Command Prompt? ... and right-clicking on it and selecting "Run As Administrator". It's the same thing as getting administrative privileges in the little Windows dialog box. If you're not Administrator, Apache will usually just exit without saying anything, which confused me for quite some time.

    Install it as service now by typing c:\apps\apache24\bin>httpd -k install and it should now be in the Services list. Feel free to set it to start automatically, or Automatic (delayed), or whatever. Feel free to add c:\apps\apache24\bin to your PATH variable.
  7. If you run into problems with Apache, either now or during the PHP installation, you can always try the following steps:
    • Try to start apache manually. Run the httpd.exe file, and see what happens. It should log things that go bad on the console. Press CTRL-BREAK to exit. Turn the Apache service off before doing this, otherwise it won't start.
    • Look in the c:\apps\apache24\logs\error.log file for warnings or errors.

Install PHP

Fortunately, PHP comes with an installer. So run it, let it go through, and it should modify everything on its own. Make sure to enable any nice modules that you need - I like the PDO MySQL libraries.

When it's done, it should have added a few lines at the bottom of the httpd.conf file, something like

LoadModule php5module "c:/program files (x86)/php/php5apache24.dll" AddHandler application/x-httpd-php .php PHPIniDir "c:/program files (x86)/php"

If it didn't, add those yourself, and make sure the paths point right.

  1. Always test the setup by running php.exe manually. If there are any problems, it should display right on the screen when it starts. Press CTRL-BREAK to exit.

    Problems with PHP usually are caused by PHP not finding the correct DLL files. Make sure that you don't have any other installation of PHP somewhere on the drive, that might interfere. Check the system PATH variable to make sure it includes the correct c:\program files (x86)\php folder (or wherever you installed it).
  2. Restart apache and verify that it doesn't display a ton of errors. Running httpd.exe manually is a good way to test it.
  3. Edit the php.ini file. Since it's under c:\program files (x86), it probably has the same modifying restrictions on it as the hosts file under windows, so you may have to enable "Modify" security rights on it as well.

    Some of the things to look for are short_open_tag = On Makes you able to use just <? instead of <?php to escape into PHP mode. May interfere with XML files if you ever run XML through PHP, for some reason. I never do, and I like the shorter layout. display_errors = On You'll need this, otherwise PHP won't display errors in your code, and on your development machine, you want it. date.timezone = America/New_York If you haven't set the timezone, you'll probably want to do so. If you don't know the available time zones, look at http://php.net/manual/en/timezones.php ... in fact, keep that link around because it's got a ton of important stuff for function calls, etc. NetBeans usually comes with a pretty good PHP reference these days, but you might want to download the documentation from http://www.php.net/download-docs.php - get the "HTML Help File (with user notes)" and once you've downloaded it, right-click on it and deselect "This file came from another computer" under the Properties page.

    Whenever you change the php.ini file, you have to restart apache. Do so now.

MySQL

No, I haven't said anything about MySQL so far, and I don't think I will either, because it's wicked easy to setup. The installer pretty much takes care of everything. And PHP has lots of good ways to talk to MySQL - I recommend either the mysqli module or the PDO::mysql module, they're good.

The only thing you should be aware of is that if you try to connect to MySQL at localhost, and it takes a good couple of seconds to do so, try connecting to 127.0.0.1 instead. For some reason, PHP tries using IPv6 first, and that usually doesn't work all that well. I always write the IP address instead of "localhost" directly into my configuration strings these days.

Ready to start developing?

Now we should be ready to start developing. Install NetBeans, and create a PHP project in the folder where you want it - like, you know, c:\apps\web\blog\public or something. Or, like I like to do, create a project in c:\apps\web\blog and then right-click on the project, and force the public root path to the public subfolder.

Create a small index.php file, and put some stuff in there, like

<?php

echo 'Hello world!';

Hopefully, now, when you open up your web browser and type in http://blog.local/, you should see the words "Hello world" on the screen. Make sure you type in the http:// part, because otherwise the browser usually searches the web for "blog.local" and it won't find anything. Once you've given it the full address a couple of times, it usually catches on.

Deploying your code to production servers

The best thing with PHP is that it pretty much runs without a hitch, no matter which server you deploy it to. There's practically no configuration - just copy, and it works. Bangarang!

By far the best method I know of deploying code to other servers, is to use a program called Beyond Compare from http://www.scootersoftware.com/ ... buy it, and love it. It is the best comparison software I've seen. Set up a directory comparison between your local c:\apps\web\blog folder, and the FTP or SFTP directory of your host. And then, let it compare all your files, synchronize the changed ones over to the server, test it, and you're live.

Other good things to use

  • For easy access to MySQL, download HeidiSQL from http://www.heidisql.com/download.php ... and then donate $10 to the author. The program is worth it.
  • Use Chrome and the Developer Tools section in Chrome by pressing CTRL-SHIFT-I. Use it a lot.
  • Look at jQuery and Bootstrap too. jQuery UI is a little bloated, I think, but works fine too.
  • Do yourself a favor and download WinLess. It helps you write LESS files instead of CSS files, which is much nicer, and it minifies your CSS code too. NetBeans also supports LESS files these days.
  • Use a good PHP framework, like Zend, CakePHP, or something else. I also wrote a framework myself (*shameless plug*) which is called Nifty, which is in a state of perpetual beta and rather poorly documented, but that's mostly because I seem to be the only one in the entire world using it. I'd use something more popular, but I've grown to like it so much that I can't imagine coding without it. It's beautiful. (Well, I'm the daddy, so I'm biased.)

That's it!

If you run into problems, email me and let me know. If I feel gracious enough, I might respond by updating the documentation. I probably won't help you look through your configuration files. If you haven't read the documentation for Apache or PHP before emailing me, I will mock you and taunt you. But there is a small chance that I might have left something out of this guide, so ... well ... fine, email me.

Especially since I haven't written any commenting functionality on this blog yet.

Updates

  • 2013-11-28 ... forgot to change some stuff in the vhosts.conf file, and added a small section about having Administrator rights for install apache services.

The Story of Michelangelo and Queen Arabella the Great

A work in progress...

Listen, I now will tell you a story fair, Of queen Arabella in a distant land Her wisdom as wonderful as the golden hair That playfully flowed over shoulders bare - Like sunrays glittered every strand. Her knowledge had no discernful bound; Every problem graceful answer found And loving eyes of coolest blue Like radiant sapphire in their hue Shone from her head with emerald crowned. And a multitude of roses gently sprung From softest soil onto her castle walls Which made of whitest stone so nobly swung In rosy arches over ancient halls; And through those halls her silvery laughter Rippled like streams in early spring And gave joyous voice to every living thing That dancingly followed her thereafter. Throughout all the kingdom spread her fame And from far beyond the people came To see the glory behind Arabella's name.

...to be continued

I Wish I Had a 9mm Gun

(written 2010-07-25)

I wish I had a 9mm gun! With such a thing, I know I would have fun! I could wave it 'round, And make a deaf'ning sound, And act somewhat like Attila the hun! I wish I had a nuclear submarine Gliding 'round, so perfectly unseen! I'd launch a nuke or two, Rogue nations to subdue And laugh inside my evil aqua-machine! ...or maybe not. It's hard, an evil genius to be When there's so much beautiful to see. - A flapping butterfly; White clouds in big, blue sky - And the love of Christ for such a nut as me.

En dikt, i respons till ett brev från Transportstyrelsen

(skriven 2010-09-24)

Se här, här är nu en speciell adress Som kommer orsaka mycket stress! En som i vårt system ej får plats! Vem, säg vem, är denne Mats? Utomlands han flyttat, sägs det här Nej, oj, det blir bara till besvär! Hör fellistor i vår central skrivs ut, Och ingen här kan fatta nåt beslut, För denne Mats, som flyttat västerut! Nej, nu slår vi stopp - stämpla! - fram med Blankett arton sjuttiofem, och ge besked! Fyll i på både bredden och på tvären! Kontrollera noggrannt alla formulären! Nej, på pappersexercisen finnes inget slut, För denne Mats, som flyttat västerut. 

Hej och hå.

An Autumn Poem

Watching all the brightly-colored leaves On bright & sunny autumn days And how they're falling off the trees In their whirlwind kind of dancing ways Makes me think of how this gentle Browned-eyed woman swept into my life And in ways most accidental Through a strange and wonderous grace You become my cherished wife.

You are beautiful, mysterious, and sweet Your love of cheese has seen no bound - Your grace has swept me off my feet And in your arms, a home I've found. So with these words, remember this Said from a heart so full of bliss In every possible way I love you, today You make my heart complete.

MySQL Function to Calculate Excel- (or Delphi)-style Dates

drop function if exists exceldate;

delimiter //

-- Function that returns an Excel-style or Delphi-style date value
-- from a MySQL date. A date value of 0 represents 1899-12-30.
create function exceldate(p_date date)
    returns int
    sql security invoker
begin
    return to_days(p_date) - 693959;
end //

delimiter ;

select '1899-12-31', exceldate('1899-12-31')     -- should be 1
union
select '2011-10-18', exceldate('2011-10-18');    -- should be 40834

Luhn (mod 10) Check Digit Algorithm in MySQL

drop function if exists luhn;
drop function if exists luhn_check;

delimiter //

-- Function that calculates a Luhn (mod 10) check digit from a numeric string.
-- The behavior is undefined if the string contains anything else than digits.
-- Assumes that the string does not have a check digit added yet, so it starts
-- with a weight of 2 at the last digit.
create function luhn(p_number varchar(31))
    returns char(1)
    sql security invoker
begin
    declare i, mysum, r, weight int;

    set weight = 2;
    set mysum = 0;
    set i = length(p_number);

    while i > 0 do
        set r = substring(p_number, i, 1) * weight;
        set mysum = mysum + if(r > 9, r - 9, r);
        set i = i - 1;
        set weight = 3 - weight;
    end while;

    return (10 - mysum % 10) % 10;
end //

-- Check if a numeric string has a valid check digit. Does this by cutting off
-- the last digit, recalculating the Luhn check digit, and comparing the strings.
create function luhn_check(p_number varchar(32))
    returns boolean
    sql security invoker
begin
    declare luhn_number varchar(32);
    
    set luhn_number = substring(p_number, 1, length(p_number) - 1);
    set luhn_number = concat(luhn_number, luhn(luhn_number));
    
    return luhn_number = p_number;
end //

delimiter ;

Common ffmpeg parameters

Information

-codecs            Display codecs
-formats           Display formats
-f fmt             Force format "fmt" (-f mp4)
-i filename        Set input file name
-y                 Overwrite output file
-t secs            Force duration to specific length (hh:mm:ss[.xxx] syntax works)
-fs limit          Set file size limit
-ss secs           Seek to given time position (hh:mm:ss[.xxx] syntax works)
-target type       Specify target type ("vcd", "svcd", "dvd", "dv", "dv50", "pal-vcd" etc) ... all the format options are set automatically

Video

-aspect aspect     Set aspect ratio (4:3, 16:9 etc)
-b:v bitrate       Video bitrate in bps
-r fps             Set frame rate (default 25)
-s WxH             Set frame size (default same as source)
-pass n            Multipass rendering (1 or 2)
-vcodec            Force video codec (vcodec h264)
-vn                Disable video altogether
-crf               Constant rate factor (between 18-25 seems good for medium grade)

Audio

-ar freq           Audio frequency (default 44100 Hz)
-b:a bitrate       Audio bitrate in bps (default 64k)
-aq quality        Audio quality (codec-specific, VBR)
-ac channels       Audio channels (downsample to stero: -ac 2)
-an                Disable audio
-acodec            Force audio codec

Subtitles

-scodec            Force subtitle codec ("copy" to copy stream)
-sn                Disable subtitles

Filters

-vf "transpose=1"  Rotate input 90 degrees
-vf scale=x:y      Scale video (320:-2 means scale width to 320, -2 round to nearest h/2)

Samples

# Extract frames between 03:00 and 03:01
ffmpeg -i file.mp4 -ss 03:00 -t 1 -y snap-%05d.jpg  

# Good for squeezing movies onto small devices
ffmpeg -i file.mp4 -y -crf 24 -vcodec h264 -f mp4 -r 25 -ac 2 -b:a 64k -vf scale=320:-2 out.mp4

My Idea for a Grand Opera

STAGE: A rural village in feudal Japan. A small, traditional Japanese house is nearby. Some cherry trees can be seen in the distance.

ACT ONE:

A samurai warrior enters the stage, inspecting his lands and his village. He starts singing the first aria of the opera, "A Sacred Land, A Sacred Call", extolling the virtues of the samurai and the honor that lies with his profession. "To die for the emperor", he sings, "a duty; an honor - oh that I would be found worthy of doing so". Two women nearby sit kneeling with their heads bowed in deep respect for this great warrior. After he is gone, they discuss the theme of the aria between them, and how they are, like him, honor-bound in their call to serve. "This is the Meaning of Life", they sing together in wonderful, tear-jerking duet.

But events are afoot. The samurai's son suddenly enters the stage, looking for his father after many years away in Kyoto. They meet; father is delighted to see him and wonders how he has been doing. Alas, it is soon revealed, that the son has not followed in his father's footsteps; he has become a traveling salesman for Hershey's Chocolate Kisses. The samurai, enraged, commands him to stop immediately and storms out. The end of Act One ends with the son, singing to a sad tune on the clarinet, "How I Love Japan; But I Love Hershey's Kisses Too".

ACT TWO:

The Japanese villagers are now talking among themselves and rumor quickly spreads that the samurai's son is a salesman for Hershey's. Some of the villagers argue that chocolate, in every form, is a good thing and Japan must embrace the influences of the new world; others argue against; when suddenly the samurai himself appears. Finding that his authority is weakening, he quickly summons his son and asks him "very well, have you changed your mind?" The son refuses to leave his new profession, and the samurai, dishonored and enraged, throws him into a bamboo prison cell. "There you will stay", he bellows, "until you respect your honor!"

The drama develops when a team of Hershey Co. lawyers emerge on the scene, singing a transformation of the main theme, "What Ho, What Ho? What Transpires Here?" The samurai threatens to kill the lawyers on the spot, but they quickly produce a document signed by the Emperor himself, which no samurai can question, that Hershey's Chocolates are legitimate all over Japan by royal decree. The second act ends with the samurai father falling on his knees in shame and dishonor, crying. The villagers look on terrified.

ACT THREE:

The samurai, unable to bear the shame of his son as a traveling Hershey's salesman, has reached a decision: He will commit suicide. The villagers are mortified, and the team of Hershey's lawyers are beaten by them until they repel them by threatening to sue them for libel. The samuari ends this quarrel by stepping onto the stage with his swords; a gray, somber figure with ashen face, prepared to do his duty. His son sings his final aria to him from the bamboo prison, "Will You Not See: a New Dawn for Old Japan", but the father refuses to listen.

But by mistake, when the samurai reaches for his last sake glass, he accidentally grabs a Hershey's Chocolate Kiss instead and puts it in his mouth! Apalled at first, his countenance soon changes as he realizes he has made a dreadful mistake, and everyone soon starts laughing. He lets out his son, forgiving him with tears in his face, and the opera ends with the grand finale, the duet between father and son, singing "Here is Tradition Too", indicating that there are traditions in Hershey's Company as well, as it is in feudal Japan, and that both can coexist together through honor and mutual respect. The villagers and lawyers combine in a final, grand chorus. The sun sets over the cherry trees which are now blossoming in full, and the curtain falls. The End.

I Had a Strange and Curious Dream

I had a strange and curious dream last night I dreamt my heart grew wings and then took flight Flew far and wide o'er hills and fields of green Above those golden clouds, to things unseen - Plucking roses sweet that never grew And singing childish rhymes all while I flew. I touched the sky, I think, with outstretched hand Drew fine-art paintings in the ocean sand And swiftly sailed across the deep blue sea To search for wonders named in poetry. In this dream I had, I know it's strange I dreamt I leaped across a mountain range! The pinnacles where draped in snowy white And gleaming in the shining sun so bright. But linger long up there was not for me, What worlds that lay beyond I had to see. Quickly went I down the mountain side T'was there I saw the meadows open wide. I stood in flowery grass so tall and green Such wonderful flowers; I had never seen! The field rolled gently down towards a stream Beyond which a city seemed to softly gleam. It was a city made of purest gold Of its glory every psalm and hymn had told. At length I stood there, taking in the sight - Until a voice spoke softly to my right. The joy I felt I cannot ever tell Suffice to say, on my knees I quickly fell. And on my cheeks, round tears then softly broke As He, my King of Kings, so sweetly spoke. "This is the home", He said, "for you I made;" "Do not fear, the price has all been paid." And from His love I then began to weep - T'was then, that I awoke out of my sleep. I had a strange and curious dream last night I dreamt my heart grew wings and then took flight. The day will come again when I will fly Beyond those golden clouds in sunset sky. And nevermore will I this planet roam For somewhere over there is home, sweet home.

Speeding Up Delphi's TStringList.IndexOf

Delphi's TStringList is one of the objects I love the most. If it's sorted (StringList.Sorted := true) then you can use it to parse huge chunks of data quickly.

For instance, looping through an enormous amount of IP addresses and keeping count of how many times they appeared, is easily done using the following code (not compiled or checked for syntax errors):

ls := TStringList.Create;
ls.Sorted := true;
for ip in ipAddresses do begin
  n := ls.IndexOf(ip);
  if n = -1 then
    ls.AddObject(ip, TObject(1))
  else
    ls.Objects[n] := TObject(Integer(ls.Objects[n]) + 1);
end;

It's very efficient. Since TStringList.IndexOf always does a binary search, it operates in log2(n) time, and using Objects as integers allows us to keep track of count without messing with the string data.

But there are things we can do to speed it up. For instance, TStringList.IndexOf relies on TStringList.Find, which itself uses AnsiCompareStr, which is a slow Windows call, taking locale and its mother into consideration. Overriding this with our own method should be worthwhile. (The code below is adapted straight from the Classes unit.)

type
  TStringListEx = class(TStringList)
  public
    function Find(const S: string; var Index: Integer): Boolean; override;
  end;

function TStringListEx.Find(const S: string; var Index: Integer): Boolean;
var
  L, H, I, C: Integer;
begin
  Result := False;
  L := 0;
  H := Count - 1;
  while L <= H do
  begin
    I := (L + H) shr 1;
    C := CompareStr(Get(I), S);
    if C < 0 then L := I + 1 else
    begin
      H := I - 1;
      if C = 0 then
      begin
        Result := True;
        if Duplicates <> dupAccept then L := I;
      end;
    end;
  end;
  Index := L;
end;

We've replaced AnsiCompareStr with Delphi's own CompareStr, which is a highly optimized FastCode routine. There are some drawbacks - things will always be sorted in byte order and no case-sensitivity is done. But we don't care about this - it can always be done afterwards; right now, speed is the main importance.

And it turns out that using the above code, in pure examples, can slash execution time with up to about 80%. Dramatic savings, indeed. In my own example, where I analyze ftp log data, I managed to cut execution time on 122 MB of data down from 7 seconds down to 3.1 seconds.

Best of all, since TStringList.Find is declared virtual, we don't need to change any types anywhere, just do a TStringListEx.Create instead of a TStringList.Create and you're good to go.

Jag har befriat dig

Se örnen den flyger, högt över land Svävar i vinden, vid klippornas rand Sträcker vingar mot solens glans Fruktar ej något, den vet den är fri.

Som örnen som flyger, så är du fri Mitt barn, Jag har kallat dig in i min frid Du behöver ej frukta, Jag är ju här Jag håller dig nära, Min hand dig bär.

Jag har befriat dig, så sväva i tro Jag har befriat dig, så vila i ro För Jag är Jehovah, din mäktige far Jag håller dig nära, hos dig blir jag kvar.

Jag såg dig bunden, med bojor av järn Stapplande framåt, med blödande sår Jag grät och jag kunde väl ej låta bli Att dö för din synd, och göra dig fri.

Och när stormen kommer, så frukta då ej När allt kring dig faller, så lita på mig När klipporna krossas, och marken ej bär När skyarna rämnar, se, då är Jag där.

Jag har befriat dig, så sväva i tro Jag har befriat dig, så vila i ro För Jag är Jehovah, din mäktige far Jag håller dig nära, hos dig blir jag kvar.

Would Be That My Dreams Took Flight

Ich halte meine Träume in meiner Hand Und frage mich, warum fliegt ihr nicht? Ihr liegt nur da, so leise und still Keine Bewegung in meinem Gesicht. Wenn sie endlich Flucht ergreifen Wie schön wär' es nicht! Hoch über die Erde fliegen sie Hoch und stolz in meinem Gesicht. Bleibt noch bei mir, Träume still Wacht noch, vergesst mich nicht. Euere Stunde kommt. Seid bereit! Bald fliegt ihr hoch, in aller Gesicht.

Things I Never Knew Could Be Done With Cmd.Exe

I spent a part of yesterday looking through all the commands in Cmd.Exe (the Windows command shell, inheritor of the old COMMAND.COM). It's interesting how they've added switches and stuff to improve on it since the early days, but how almost nobody seems to use it. Admittingly, it's a far cry from bash, the Unix command shell, but there's a few old tricks you still can pull out of the hat. People may flame it and despise it, but I always thought you should be able to do more with the good ol' shell.

Here's a few of the things I found ... in alphabetical order.

ATTRIB [/s] [/d]

Changes file attributes. /s makes it recursive, /d makes it operate on directories as well. I didn't know about these switches before. Handy.

CALL :label arguments

Neither did I know you could call a label in a batch file. This should make it easier to write "gosub"-like routines. And the parameters can be expanded with new interesting features, see below. To exit from the subroutine, use the "goto :eof" statement.

CHKNTFS

Schedule a check-disk on next boot. Might come in handy sometime.

EXIT /b [errorlevel]

Exits the command shell. If you use the /b switch, exits the current batch file. You can also pass an errorlevel along.

F7

Pressing F7 brings up the history list. How come I never knew that?

FINDSTR [/r] [/c:]"search string" filespec

Find strings in files. I might still use Turbo Grep, but this is cool too. Normally it searches using an OR pattern on the search string (meaning "I love you" finds all instances of "I", "love" or "you"), use the /c: switch to make it an AND search.

The /r parameter turns the search string into a regexp. Note, some of the fancier stuff might not work as usual, consult the FINDSTR /? or the online help for further information.

FOR %%v IN (set) DO ...

The FOR command is one of the coolest features in batch programming. I had no idea you could do so much with it.

FOR %%f IN (dpr pas dfm res) DO COPY *.%%f \deploy

Copy all Delphi source files for a project to a specific directory.

FOR /d %%d IN (set) DO ...

Match directories in wildcards instead of files.

FOR /r [path] %%v IN (set) DO ...

Recursive operation on files found, optionally operating relative to "path" instead of the current directory. It might be used like "FOR /r c:\deploy %%f IN (.) DO ATTRIB -r %%f", which will recursively remove the read-only attribute from all files in c:\deploy.

FOR /l %%v IN (start, step, end) DO ...

For-loop. "FOR /l %%v IN (1, 1, 5)" gives the sequence 1 2 3 4 5.

FOR /f ["options"] %%v IN (file-set | "string" | 'command') DO ...

The /f parameter is probably the most interesting feature I've found. It reads lines from an input file, string, or result from a shell command, tokenizes them and processes a command for each line. Normally, the token delimiters are space and tab, and it usually operates on the first token found, so without extra options you will always get the first word in each line. But it can be modified with the following options:

eol=c Set the end-of-line character. One character only.
skip=n Skip n lines in the beginning.
delims=xxx Delimiter set, default is space and tab.
tokens=x,y,n-m Which tokens to feed into the command. Variables start at the variable given, and allocates further as needed in alphabetical order. * means "the rest of the line".
usebackq Use backticks instead of apostrophe for the command evaluation. The format changes to ("file-set" | 'string' | `command`). Required if you use filenames with spaces.

FOR /f "eol=; tokens=2,3* delims=, " %%i IN (myfile.txt) DO @ECHO %%i %%j %%k

Parse each line in myfile.txt, ignoring lines that begin with semicolon, pass 2nd and 3rd tokens into the command, separating each token my either comma or space. Notice how the sequence goes: %i, %j, %k.

FOR /f "usebackq delims==" %%i IN (set) DO @ECHO %%i

Enumerate all variables found.

Expansion of variable parameters is also available, see below.

GOTO :eof

Jump to end of file. Handy way of exiting from a script.

IF [NOT] ERRORLEVEL n ..

IF [NOT] EXIST filename ...

IF [NOT] string1==string2 ...

IF [/i] string1 EQU|NEQ|LSS|LEQ|GTR|GEQ string2 ...

The normal IF command is enhanced, too. It can check errorlevels as before, file existance, and compare strings. But is also has new operators. For instance, the "IF ERRORLEVEL 3" statement can now be written "IF %ERRORLEVEL% LEQ 3".

/i means case-sensitive (or case-insensitive, I forgot which). Numeric strings evaluate as numbers, not strings.

IF now also supports multi-line statements and ELSE statements, see below.

MD \a\b\c\d

Will create new directories in sequence.

MORE /e

Extended more. These keys are available:

P n - next n lines S n - skip n lines F - next file Q - quit = - show line number ? - show help line space - next page return - next line

MORE +n

Start from line n.

PUSHD \\server\path

Create a temporary drive allocation, starting from Z:, for the particular UNC path. This will be cleared with POPD.

RD /s /q

Very dangerous command.

SET [var[=[value]]]

SET /a [var=]expression

SET /p var=[prompt]

SET only will display all variables. SET P will display all values starting with P. SET P= will clear variable P.

SET /a will perform a calculation, for instance SET /a X=2*2 + 5.

SET /p will prompt for user input and store the result in a variable.

SETLOCAL / ENDLOCAL

Make local changes to the environment. Work all you want with it, then call endlocal to revert back to where you were. Also handy, especially with some of the advanced SET features.

SHIFT [/n]

Shift parameters. Optionally start at the nth position, preserving all elements %0 .. %(n-1).

SORT [/+n] [/o outfile]

Sort may start sorting at the nth position now. Could be good for unwanted stuff in the beginning (timestamp in logs, perhaps). /o is faster than piping.

Interesting ways of treating variable expansion

Some new ways of treating variables are available. Like, string substitution and substring matching.

%PATH:str1=str2% Substitute all occurrences of str1 with str2.
%PATH:~10,5% Substring, start at position 10 and extract 5 characters.
%PATH:~-10% Only get the last 10 characters.
%PATH:0,-2% Extract all but the last 2 characters.
 
%CD% The current path
%DATE% Current date
%TIME% Current time
%RANDOM% A random number between 0..32767.
%ERRORLEVEL% The current errorlevel.
 
%* All arguments
%0 .. %9 Arguments
%~1 Remove quotes from parameter 1
%~f1 Expand to fully qualified filename
%~d1 Expand to drive letter only
%~p1 Expand to path only
%~n1 Expand to file name only
%~x1 Expand to extension only
%~s1 Expand to short file name only
%~a1 Expand to file attributes
%~t1 Expand to file date/time
%~z1 Expand to file size
%~$PATH:1 Search through all directories specified in %PATH%. If the file is found, return the fully qualified filename in that directory. If the file isn't found, return blank.
%~dp1 Expand to drive and path. (Further elements may be combined: %~ftza1 gives a DIR-like output)

Interesting ways of doing IF and FOR statements

There's a syntax I've never seen either with IF and FOR statements. You can use IF-ELSE-syntax in this way:

IF EXIST hello.txt (     DEL hello.txt ) ELSE (     ECHO hello.txt is missing! )

...or even...

IF EXIST hello.txt (DEL hello.txt) ELSE (ECHO hello.txt is missing!)

And how about this?

FOR /l %%v IN (1 1 5) DO (     ECHO This is line number %%v. )

The crucial thing seems to be, in ELSE statements, that ELSE has to be written "on the same line" as the IF statement. This is why ELSE is written on the same line as the parantheses.

So there you are. A whole new way of writing batch files. No extra software needed, just plain old Windows XP.

Tree

(Wrote this on the train home from Stenungsund a few years ago.)

His name was Tree.

For as long as he could remember, he had stood here in the middle of the forest. Deep in the far recesses of his slow, heavy conscience, deep memories awoke and slowly rose up to a higher level of awareness; memories of his youth, when he was a much, much younger and more vigorous Tree; memories of ages past that seemed so remote; yet he was somehow aware that they had, indeed, happened to him, only a long, long time ago.

He was almost sound asleep now. His branches hung heavily, and most of the time his mind seemed to be clouded with a thick, dreary fog from which he awoke but momentarily. He was dimly aware of coldness; among his twigs and leaves frost was setting in, and if he stretched out his senses he could just barely sense the last fading chlorophyll receding down, deep down, in bracing for winter. They were not strong emotions; over the last, say… he had some difficulty remembering… say, the last two ages or so - he defined an age to be twelve winters, a decision he made many, many ages ago when he was still curious about things - over the last two ages he had been growing increasingly numb of outside things, and now he knew that he was slowly drifting away into a deep sleep from which he would never more wake up.

But those memories, reaching him from far off, caused him to stir a little bit. He carefully - albeit rather slowly - turned his dim attention to them. It was like a taste of spring. They had an air of life about them. And then, he lost it…

Some time passed.

Suddenly something stirred him again. His mind rose sleepily again, and noted that it was colder now, much colder. He could sense that most of his leaves had disappeared; he felt it, a keen awareness of nakedness that still grasped him in his drowsy state. Deep down, something that many others might have been surprised at seeing in a tree this age, started to form; something that would best be described - in want of a better word - as, perhaps, a smile. His mind fluttered away, searching for something eerily escaping; a faint and distant memory of the first time he felt that nakedness. And then, there it was again, hitting him with much stronger force this time: Distant memories, distant songs, of ages passed.

His mind sank back as he slowly began to walk through the memories. And suddenly, there was a taste of spring, and an air of life that alerted him… the feelings came back to him again, unwilling to let go.

He remembered ages past. The sun. Yes, the sun had been stronger in those days, hadn't it? He had felt it stronger. He remembered the warm, lovely sun beating upon his leaves, drinking in every drop of warmth it induced. He remembered the sense of life pouring through him; a tickling, invigorating feeling of sorts. For a moment he sensed again the lovely feeling of dipping his roots deep, deep down in the ground, sapping up water and sensing it flow all through his trunk, out through the branches, the twigs, and his leaves… A feeling so joyful and bright that almost made him want to laugh.

He remembered that he used to sing a lot. He sang, in his own special, deep, dark and hollow voice, a song of life. He sang with the wind rustling through his leaves. He sang to the little squirrels that climbed around among his branches. He sang a song about the little birds, which made nests in him, and to the lives and events that took place. His song grew quieter as the ages slowly passed by, mostly because he didn't feel the need to sing much longer. And besides, other trees around him did so much better, too.

And yes, he remembered the little birds, especially the birds. When he was young, he remembered being annoyed by them. He was just a few winters old, and they came and sat down in him, took cover among his leaves. He was very rude in those days, and very proud too. A sense of tender joy pervaded him as he thought back on these times. Yes, he had been very proud indeed… and now, if only words could tell the wisdom he possessed.

How many ages of birds had he seen? - Too many to be counted, indeed. They had come in great numbers, built nests in him, taken cover in him, and raised their young there. Generations had come and gone, and he had harbored them all. After all, he was Tree; many animals knew him. He was, he believed, the oldest in the forest.

He had been young once. He didn't have memory of… of…? Being formed, or made, or born. Or however he came about. He had to start once, he thought. Back in his younger days he had given this thought much attention, where he came from, and perhaps, some day, where he would go. He knew by now, from observing other trees, that he would invariably, one day, sink into eternal sleep; and before long his trunk would begin to fade away, and one day he might fall over and then, well, the rest would be history; but the idea of where they all came from still actually eluded him.

His mind had been much, much faster in those days, and he had been much more sensitive. He could still remember the tickling feeling of the ants' little feet as they climbed upon his trunk and branches. He had laughed a whole lot about it in those days, and sung songs about it. He liked to sing. He never did that anymore, but he liked to do it in his younger days.

He remembered when he began keeping track of seasons. In the beginning he had known spring; that was his first memory. The fresh air, all the little sounds around him, and the little brook about his roots. In those days, those large animals used to come around and chew off bark from him, and eat. He was outraged about that back then; nowadays, that never happened. His first summer… and then fall, and winter, when all his leaves fell away and that bitter cold, nakedness and drowsiness gripped him. He had thought his end was near. But then he awoke again in spring; and so, many summers, falls, winters and springs again had passed.

He began to keep track of those cycles as they passed by. When they became too many to count he grouped them in twelve, and called them ages. And now, too many ages had passed by for him to count.

Yes, he could feel it. His conscience slowly began drifting off again, slowly letting go of all these memories. He knew that this was the final end for old Tree. Or was it? Just as he had thought once that his end was near, he had slept, and then woken up again. Maybe there was something on the other side of this sleep. Maybe there was… Maybe, one day, on some other side somewhere, Tree would wake up again, young and healthy, with fresh new leaves and shiny bark, and…

He lost that thought. He could barely stay awake. He was hardly any longer aware of anything around him; and he was so tired, so unfathomably tired… it would be good to sleep. He had had a long, healthy life, and now he would sleep. "Hark, old Tree has fallen asleep", he thought as he drifted off into the dim fogginess that crept upon him.

And then, as softly as ever - as gently as his leaves had ever waved in the cool summer's breeze, as mildly and tenderly as any fog had ever engulfed him in - old Tree fell asleep, for the last time. His conscience slowly faded; and silently - just as quiet as the rest of the forest that now lay draped in thick, white and heavy snow - so silently, Tree passed away.

The Story of e, i, pi and the Elusive Minus One

There are three magic numbers in mathematics that stand out above all else. The first one is π, the relationship between a circle and it's diameter; there's e, the natural logarithm that permeates so many areas of mathematics; and i, the square root of -1, which is an impossible and unthinkable number (for no number multiplied by itself can ever be negative).

These three numbers, e, i and π, have nothing to do with each other. They stand remote in meaning and effect from each other, all three of them deriving from different areas of mathematics and with long and different histories behind them. And yet, there is an equation that ties them together in a most astounding symmetry. It is:

e = -1

All of these magical numbers sort of, in their own special way, cancel each other out. Where there is no apparent symmetry, if you only combine them together like the final pieces of a huge puzzle, suddenly a remarkable symmetry appears: It's minus one.

I've often thought about this minus one, too. It, too, represents a form of mathematics that is unusual. You can't really measure -1. You can't go -1 yards, you can't measure -1 ounce of flour. It is a reflection of real numbers; negative, elusive, but not without its own agreeable symmetry and beauty.

Some people like to write the equation as

e + 1 = 0

This strikes me as enormously masculine, for some reason. The numbers are all positive; it's all bright and sunny, on the positive side of things, and seems to say "hey, look what a nice, happy result we get if we tweak this equation a little. We've got i, e, π, 1 and 0, and a plus sign. It covers everything fundamental in mathematics."

But I like the first way better; it seems more fundamentally feminine, in a way; almost a bit of Yin and Yang in a sense. The feminine side is not too concerned about shaping things right by force; it's happy with accepting a minus one just on its own merits. It shows you that you don't have to mangle everything to get a nice, round result from it; that things can end up with a negative one and still be beautiful in its own special way.

To me, the minus one is like a nymph in a secluded, enchanted forest, gazing lovingly down into a clear, dark pool of water at her own beautiful reflection; something that can not be touched, smelled, or sensed - just lovingly gazed at and admired from afar. And this is the ultimate beauty I see in the equation above: Three fundamental numbers in universe combined into one elusive, mystical reflection.

And they say math is boring.

The Oyster and King Bore

Back in high school, a friend in the computer club wrote a poem about an oyster who defeated King Bore (the presumed God of Snow and Winter). In a sudden flash of inspiration, I blatantly stole the idea and wrote my own.

It turned out something like this.

Once upon a time, a colony there was Of oysters, humbly admiring their cause Deeply they lived in mid-ocean hot Thus happy they were, and warm a lot. But those times were gone and forgotten The warmth had vanished, and the sunlight was rotten. Storms of winter blew over waters iced And the colony was all terrorized. "This curse must be broken", the leader then said The oysters agreed, 'cause soon they'd be dead. Then an oyster stepped forth, screaming "Coldness no more! I'll myself be the cause of the death of King Bore!" His axe on his side, and filled up with anger He bravely set out on his journey of danger. The wind tore his face, threw snow in his eyes "To h*** with the snow", he shouted, "make way for the wise!" Alas, if only wise he'd been, and not so irate He'd have watched for those who'd wait Thus captured he was, and imprisoned as well Thrown behind bars, where rats yet dwell! But bars of iron can't stop an oyster that good He'd sworn to kill, and believe me, he was in such mood! He hewed back his axe, and with one mighty stroke He shattered the bars, and the iron lock he broke! Through winding passages and tombs he ran Finding his way through the cells, like only oysters can. Before long, he stood by the entrance great To the mighty throne, where King Bore would wait. He kicked the doors open, vindictive as few Only to see King Bore, releasing the curse anew! With sinister laughs to the oyster he turned Showing grinning teeth, and eyes that burned. No time to spill, the oyster ran in hurrying pace And buried his axe deep in the Kings dastardly face. Blood drizzled as the evil God cried And left the poor corpse he'd occupied. Far over hills and fields the mighty King flew Crying aloud as fear of oysters grew. Joining the fleeing soul were winter and ice And left the earth in a choir of cries. Never again have we heard of King Bore Seemingly lost in cold space's core. And from that day, we all love our saint The hero of oysters, defeating winter faint."

The Old Mainframe Computer

I wrote this poem many years ago. I thought I might post it here.

In a dark and gloomy dungeon Racked by storms and thunder A valiant server stood on guard As lightning ripped the night asunder. It stood there lonesome and forlorn Working quietly in the freezing cold But not a tear was seen On its faithful color screen This server had a heart of gold.

In its core a dignified processor worked Although old, still bravely faithful And though its software wasn't new Its owners still were very grateful. It flawlessly performed, both day and night It ticked so quietly, so gracefully and bright A thousand users everywhere Put the server's idle time on hold But no one knew the server's heart Yes, this server had a heart of gold.

Deep down in this server's brittle chips A stable, very stable kernel ran. And through thick cables proceeding forth It quietly communicated on the WAN. Hundreds of transactions were processed On the company's intra-network site. But the only evidence of work there was Was a little light that gently flickered in the night.

And although many years had passed And newer systems came and went And elaborate technologies passed by ODBC, DCOM, Windows and Lucent Still they just failed and failed and failed And came nowhere near this server's unique mold Of faithful trust and tender hope Buried in this server's gentle heart of gold. Yes, this server had been working quite a while And if it could be said it had a mind Anyone who looked inside would find That splendid willingness to "walk the extra mile".

Then, one day, a strategic decision was made. "Windows is the future. Old IBMs are not." So this server was sadly carried away And replaced with something that was "hot". And the damp computer place was changed Into nice, air-conditioned halls With carpets, supervising systems And thin fiber cables lined the walls. And in the old computer's place now there sat An NT 5.0, spinning like a cat.

But soon enough, something happened Users called and gleefully complained Connections lost, memory faults all over And a general confidence that waned. Computers crashed, and deals weren't met And executives and computer guys began to fret. Previously trusting users collapsed and cried As their screens shone brightly blue With exception faults and many, many GPFs... It was a horrid nightmare now come true.

In the meantime, this old server sat Now abandoned, so silent and forlorn. Its kernel didn't work. No lights were on. It couldn't even mourn. The letters IBM, once bright and shining And the cover, once so neat, with silver lining From all its work so badly torn. It now simply occupied the tiny space Between a dead screen and an old computer case.

But then, as its tiny little chips gave up all hope It finally was remembered. An angry group of software engineers Brought the NT system, now dismembered. They threw the stupid thing against the wall And took the old computer through the hall. And accompanied by the staff's wild cheer On the old screen there appeared a little tear. And as it was plugged in again, Its systems flared to life once more The BIOS booted, and the kernel started up The old processes spread through its trusty core.

Then everything was back to normal. Everything ran well again. And all throughout the place, executives agreed That new technology is just in vain. That trusted systems should not be replaced, Once it worked, it continually kept administrators amazed. And deep down, in that old dungeon, now so bright That used to be forgotten and so cold There worked a valiant server, And that server had a heart of gold.

Page 1 2