0.2.2 Added context menu to results table allowing the opening of files &
Fixed incorrect text in About dialog
Replaced match whole words with exact phrase search
0.2.1 Added file viewer
0.2 First release
6.2 Replaced icons with something more suitable
Added RGB and CMYK support
Solved problems of grey edges on HSL control
Preview font colour switches between black and white depending on panel colour
6.1 Fixed colour entry bug
Added support for 3-digit rgb values (eg #f00)
6.0 Ported from Delphi to Lazarus with a near-complete rewrite
Added ability to capture any colour from the screen at any time via the tray icon
This function requires synapse.
function LookupWhois(host: String): String;
refer := '';
// First connect to whois.iana.org and find the whois server for the domain
b := TTCPBlockSocket.Create;
response := TStringStream.Create('');
b.SendString(host + #13#10);
// Find the refer: line
rlines := TStringList.Create;
rlines.Text := response.DataString;
for i := 0 to rlines.Count -1 do
if AnsiPos('refer:',rlines[i]) > 0 then
rparts := TStringList.Create;
ExtractStrings([':'], , PChar(rlines[i]), rparts);
refer := trim(rparts);
if Length(refer) < 1 then Result := 'Could not lookup whois for ' + host
// Now we connect to the referred server
b := TTCPBlockSocket.Create;
b.ConvertLineEnd := false;
// .com and .net are handled differently
if (AnsiEndsStr('.com',host)) or (AnsiEndsStr('.net',host)) then
b.SendString('domain ' + host + #13#10)
b.SendString(host + #13#10);
response := TStringStream.Create('');
Result := response.DataString;
Here is a quick and dirty way to detect Windows 10 using FreePascal.
function IsWin10: Boolean;
output := '';
Result := false;
if RunCommand('cmd.exe',['/C','ver'],output) then
if AnsiPos('[Version 10',output) > 0 then Result := true;
This function requires the Process unit to be present in your uses clause.
1.1 First release of GUI tool
1.0 First release
1.3.3 Fixed startup state spanning monitors
Improved UI layout
1.3.2 Fixed blank useragent
Added XiControls to about dialog
Rewrote multithreading so it's done properly
Fixed parsing issues with links starting ./
1.3.1 Added List tab which displays found links as a simple list
Improved overall UI responsiveness and crawl speed
1.2.1 Improved speed related issues caused by 404 checking
1.2 Fixed update notification click
Stopped 404s being parsed and listed
Resolved issue with parsing of root links with filenames
1.1 Fixed saving of useragent history
Fixed saving of URL history
Attached return key to URL textbox
1.0 Recreated project files based on Lazarus 1.5 format
Added option to set useragent
Added option to disable custom theme
Added option to change editor font
Added option to customise filetypes to be ignored
Changed ReadMe to DOS format
0.1.9 Fixed HTTPS bug
0.1.8 More improvements to HTML parsing
Added CSV output
Fixed issues with anchor links
Resolved some issues with relative links
0.1.7 Fixed problem of parsing links that contain anchors
Added ignore for skype: links
Added simple sort
0.1.6 Fixed inclusion of lone anchors in output
Fixed last known HTML parsing bugs
Removed alpha status
0.1.5 Improved HTML parsing again
0.1.4 Added XML output
0.1.3 General bug fixes
0.1.2 UI improvements
0.1.1 Improved HTML parsing
0.1 First release
This article will be comparing the speed of two different third party XML parsers for FreePascal/Lazarus and Delphi. I don't intend on going into great details about features as this is purely to test the speed of parsing a simple XML file.
All testing was done using FreePascal 3.0.0 64-bit using FreeBSD 10.3 on an Intel Pentium P6200 CPU @ 2.27Ghz.
The XML files used in tests are available here (100,000 entries) and here (1,000,000 entries). The XML files are simple records of random names and ages generated using my Random Name Generator (also written using FreePascal).
The the source files for the test themselves can be downloaded here.
For the test I am simply parsing the XML contents into a TList using the following record type:
PPerson = ^TPerson;
TPerson = record
MYTHcode XML Parser
I have been using MYTHcode XML Parser for many years now, its very simple to use and has always served me well. Sadly, it hasn't been updated in a very long time so if you're trying to use it in a recent version of Delphi you'll probably find it churns out rubbish - it still works perfectly in Lazarus/FreePascal however.
The MYTHcode XML Parser uses a standard style while Next do structure that you're likely to see in other XML Parsers. The parser object is created with a string of the XML as a parameter, as a result the XML is loaded into a TStringList before being parsed.
xml := TStringList.Create;
parser := TXMLParser.Create(xml.Text);
while parser.Next do
if Parser.TagType = ttBeginTag then
if Parser.Name = 'person' then new(person);
if Parser.Name = 'firstname' then person^.FirstName := parser.ContentCode;
if Parser.Name = 'surname' then person^.SurName := parser.ContentCode;
if Parser.Name = 'age' then person^.Age := StrToIntDef(parser.ContentCode,0);
if (Parser.TagType = ttEndTag) and (Parser.Name = 'person') then
The process is quite simple: we check for an open tag, then within that we either create a new pointer to our record type or set the relevant variable to the right value. Because it parsed on a tag-by-tag basis we can always assume the <person> tag has been found in the loops before the 3 tags containing the actual values.
Finally, when we find the closing person tag we are safe to add our record to the TList.
LibXmlParser was a library I found recently when I was forced to use Delphi for a project instead of Lazarus. It does appear to be actively developed and has full support in modern Delphi versions aswell as FreePascal/Lazarus. I am using the non-Unicode version in this test.
This library takes a different approach to parsing where each loop within the scan only focuses on the current XML segment, so the first thing I had to do was create an enum to keep track of where I was within the XML:
TCurrentTagType = (ttNone, ttFirstName, ttSurName, ttAge);
LibXmlParser does not require the XML to exist as a string before being parsed and can load direct from a file or buffer.
parser := TXMLParser.Create;
parser.Normalize := true;
while parser.Scan do
if Parser.CurPartType = ptStartTag then
currentTag := ttNone;
if Parser.CurName = 'person' then new(person);
if Parser.CurName = 'firstname' then currentTag := ttFirstName;
if Parser.CurName = 'surname' then currentTag := ttSurName;
if Parser.CurName = 'age' then currentTag := ttAge;
if Parser.CurPartType = ptContent then
case currentTag of
ttFirstName: person^.FirstName := parser.CurContent;
ttSurName: person^.SurName := parser.CurContent;
ttAge: person^.Age := StrToIntDef(parser.CurContent,0);
if (Parser.CurPartType = ptEndTag) and (Parser.CurName = 'person') then
As you can see in this parser I am having to use the enum to keep track of which tag I am inside of to extract the contents - whilst this is a perfectly valid method it doesn't "feel" natural to me and could turn in to a real nightmare when handling XML that has many fields.
First up I tested both parsers with an XML file containing 100,000 records:
As you can see from the above screenshot MYTHcode XML Parser came out on top by almost half a second. I ran the test again with 1,000,000 records.
And again the MYTHcode parser came out on top although there were signs of it slowing down: LibXmlParser managed to keep its time almost exactly 10 times that of the first run but MYTHcode ran almost 1% slower with the larger dataset.
The MYTHcode parser, on the face of things, does seem to be faster than LibXmlParser although niether seem to have functionality for loading from streams which means the XML has to exist in memory before it can be parsed; this is an issue when using very large datasets. MYTHcode Parser's downside is that it doesn't appear to be actively maintained so if you're using a modern version of Delphi you'll definitely want to avoid.
Creates an XML file containing a list of random names and ages up to the value
specified in TOTALCOUNT (line 10).
Requires FreePascal to build and for both firstnames.txt
and surnames.txt to be in the same directory.
After installing FreePascal, simply run the following command to build:
# fpc createnameslist.pas
Then execute the created createnameslist[.exe] binary.
Example output (TOTALCOUNT = 3):
<?xml version="1.0" encoding="UTF-8" ?>
For the latest version of the source and the required text files, visit github.
Here is a nice and simple way to follow HTTP redirects using the Ararat Synapse THTTPSend class.
uses Classes, SysUtils, httpsend;
function HTTPGet(URL: String; UserAgent: String): String;
HTTP := THTTPSend.Create;
HTTP.UserAgent := UserAgent;
realurl := '';
l := TStringList.Create;
// Perform HTTP request
if HTTP.HTTPMethod('GET', URL) then
// If its a 301 or 302 we need to do more processing
if (HTTP.ResultCode = 301) or (HTTP.ResultCode = 302) then
// Check the headers for the Location header
for i := 0 to HTTP.Headers.Count -1 do
// Extract the URL
if Copy(HTTP.Headers[i], 1, 8) = 'Location' then
realurl := Copy(HTTP.Headers[i], 11, Length(HTTP.Headers[i]) - 11);
writeln('Redirecting to ', realurl);
// If we have a URL, run it through the same function
if Length(realurl) > 1 then l.Text := HTTPGet(realurl, UserAgent);
// Return result
Result := l.Text;
// Clean up
url := 'http://matthewhipkin.co.uk';
writeln('Checking ', url);
response := HTTPGet(url, 'Mozilla/5.0 (X11; FreeBSD amd64; rv:50.0) Gecko/20100101 Firefox/50.0');
Tested with FreePascal 3.0.0 but should work fine on Delphi.
Well, as it's a new year I thought I'd have a go at this blogging lark.
Every so often I might have a thought or two about some random subject, and who knows I might even manage to post about it here!
I'm still fiddling with the system - I always liked having the latest application releases on the home page so need to decide how I'm going to integrate those into the blog.