
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1418 8e941d3f-bd1b-0410-a28a-d453659cc2b4
402 lines
9.8 KiB
ObjectPascal
402 lines
9.8 KiB
ObjectPascal
{Version 9.45}
|
|
unit URLSubs;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils,
|
|
{$IFNDEF LCL}
|
|
WinTypes, WinProcs, Messages,
|
|
{$ELSE}
|
|
LclIntf, LMessages, Types, LclType, HtmlMisc,
|
|
{$ENDIF}
|
|
htmlun2;
|
|
|
|
function GetBase(const URL: string): string;
|
|
{Given an URL, get the base directory}
|
|
|
|
function Combine(Base, APath: string): string;
|
|
{combine a base and a path taking into account that overlap might exist}
|
|
{needs work for cases where directories might overlap}
|
|
|
|
function Normalize(const URL: string): string;
|
|
{lowercase, trim, and make sure a '/' terminates a hostname, adds http://}
|
|
|
|
function IsFullURL(Const URL: string): boolean;
|
|
{set if contains a protocol}
|
|
|
|
function GetProtocol(const URL: string): string;
|
|
{return the http, mailto, etc in lower case}
|
|
|
|
function GetURLExtension(const URL: string): string;
|
|
{returns extension without the '.', mixed case}
|
|
|
|
function GetURLFilenameAndExt(const URL: string): string;
|
|
{returns mixed case after last /}
|
|
|
|
function DosToHTML(FName: string): string;
|
|
{convert an Dos style filename to one for HTML. Does not add the file:///}
|
|
|
|
function DosToHtmlNoSharp(FName: string): string;
|
|
{convert a Dos style filename to one for HTML. Does not add the file:///.
|
|
use where "#" is not being used as a local position but rather is part of filename.}
|
|
|
|
procedure ParseURL(const url : String; var Proto, User, Pass, Host, Port, Path : String);
|
|
{François PIETTE's URL parsing procedure}
|
|
|
|
implementation
|
|
|
|
{----------------GetBase}
|
|
function GetBase(const URL: string): string;
|
|
{Given an URL, get the base directory}
|
|
var
|
|
I, J, LastSlash: integer;
|
|
S: string;
|
|
begin
|
|
S := Trim(URL);
|
|
J := Pos('?', S);
|
|
if J > 0 then
|
|
S := Copy(S, 1, J-1); {remove Query}
|
|
J := Pos('//', S);
|
|
LastSlash := 0;
|
|
for I := J+2 to Length(S) do
|
|
if S[I] = '/' then LastSlash := I;
|
|
if LastSlash = 0 then
|
|
Result := S+'/'
|
|
else Result := Copy(S, 1, LastSlash);
|
|
end;
|
|
|
|
{----------------Combine}
|
|
function Combine(Base, APath: string): string;
|
|
{combine a base and a path taking into account that overlap might exist}
|
|
{needs work for cases where directories might overlap}
|
|
var
|
|
I, J, K: integer;
|
|
Proto: string;
|
|
begin
|
|
Base := Trim(Base);
|
|
Proto := GetProtocol(Base);
|
|
J := Pos('file:///', Base);
|
|
if J = 1 then
|
|
J := Pos('/', Copy(Base, 9, Length(Base)-8))+8 {fourth slash for file:///c|/ }
|
|
else
|
|
begin
|
|
J := Pos('://', Base);
|
|
if J > 0 then
|
|
J := Pos('/', Copy(Base, J+3, Length(Base)-(J+2)))+J+2 {third slash for http://www.xxx.com/ }
|
|
else
|
|
J := Pos('/', Base);
|
|
end;
|
|
{insure there is a '/' on the end}
|
|
if J = 0 then
|
|
begin
|
|
Base := Base+'/'; {needs a slash}
|
|
J := Length(Base);
|
|
end
|
|
else if Base[Length(Base)] <> '/' then
|
|
Base := Base + '/';
|
|
|
|
APath := Trim(APath);
|
|
|
|
if (APath <> '') and (APath[1] = '/') then
|
|
begin {remove path from base and use host only}
|
|
if Pos('//', APath) = 1 then {UNC filename}
|
|
if Proto = 'file' then
|
|
Result := 'file:///' + APath
|
|
else
|
|
Result := Proto+':'+APath
|
|
else
|
|
Result := Copy(Base, 1, J) + Copy(APath, 2, Length(APath)-1);
|
|
end
|
|
else Result := Base+APath;
|
|
|
|
{remove any '..\'s to simply and standardize for cacheing}
|
|
I := Pos('/../', Result);
|
|
while I > 0 do
|
|
begin
|
|
if I > J then
|
|
begin
|
|
K := I;
|
|
while (I > 1) and (Result[I-1] <> '/') do
|
|
Dec(I);
|
|
if I <= 1 then Break;
|
|
Delete(Result, I, K-I+4); {remove canceled directory and '/../'}
|
|
end
|
|
else
|
|
Delete(Result, I+1, 3); {remove '../' after host name}
|
|
I := Pos('/../', Result);
|
|
end;
|
|
{remove any './'s}
|
|
I := Pos('/./', Result);
|
|
while I > 0 do
|
|
begin
|
|
Delete(Result, I+1, 2);
|
|
I := Pos('/./', Result);
|
|
end;
|
|
end;
|
|
|
|
function Normalize(const URL: string): string;
|
|
{trim, and make sure a '/' terminates a hostname and http:// is present.
|
|
In other words, if there is only 2 /'s, put one on the end}
|
|
var
|
|
I, J, LastSlash: integer;
|
|
Query: string;
|
|
begin
|
|
Result := Trim(URL);
|
|
J := Pos('?', Result);
|
|
if J > 0 then
|
|
begin
|
|
Query := Copy(Result, J, Length(Result)-J+1);
|
|
Delete(Result, J, Length(Result)-J+1);
|
|
end
|
|
else Query := '';
|
|
if Pos('file://', Result) = 1 then
|
|
Result := DosToHTML(Result);
|
|
if GetProtocol(Result) = '' then
|
|
Result := 'http://'+Result; {add http protocol as a default}
|
|
J := Pos('/./', Result);
|
|
while J > 0 do
|
|
begin
|
|
Delete(Result, J+1, 2); {remove './'s}
|
|
J := Pos('/./', Result);
|
|
end;
|
|
J := Pos('//', Result);
|
|
LastSlash := 0;
|
|
for I := J+2 to Length(Result) do
|
|
if Result[I] = '/' then LastSlash := I;
|
|
if LastSlash = 0 then
|
|
Result := Result+'/';
|
|
Result := Result+Query;
|
|
end;
|
|
|
|
function IsFullURL(Const URL: string): boolean;
|
|
var
|
|
N: integer;
|
|
S: string;
|
|
begin
|
|
N := Pos('?', Url);
|
|
if N > 0 then
|
|
S := Copy(Url, 1, N-1)
|
|
else S := Url;
|
|
N := Pos('://', S);
|
|
Result := (N > 0) and (N < Pos('/', S)) or (Pos('mailto:', Lowercase(S)) <> 0);
|
|
end;
|
|
|
|
function GetProtocol(const URL: string): string;
|
|
var
|
|
User, Pass, Port, Host, Path: String;
|
|
S: string;
|
|
I: integer;
|
|
begin
|
|
I := Pos('?', URL);
|
|
if I > 0 then S := Copy(URL, 1, I-1)
|
|
else S := URL;
|
|
ParseURL(S, Result, user, pass, Host, port, Path);
|
|
Result := Lowercase(Result);
|
|
end;
|
|
|
|
function GetURLExtension(const URL: string): string;
|
|
var
|
|
I, N: integer;
|
|
begin
|
|
Result := '';
|
|
I := Pos('?', URL);
|
|
if I > 0 then N := I-1
|
|
else N := Length(URL);
|
|
I := Pos('#', URL);
|
|
if (I > 0) and (I < N) then N := I-1;
|
|
for I := N downto IntMax(1, N-5) do
|
|
if URL[I] = '.' then
|
|
begin
|
|
Result := Copy(URL, I+1, N-I);
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function GetURLFilenameAndExt(const URL: string): string;
|
|
var
|
|
I: integer;
|
|
begin
|
|
Result := URL;
|
|
for I := Length(URL) downto 1 do
|
|
if URL[I] = '/' then
|
|
begin
|
|
Result := Copy(URL, I+1, 255);
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
{ Find the count'th occurence of the s string in the t string. }
|
|
{ If count < 0 then look from the back }
|
|
{Thanx to François PIETTE}
|
|
function Posn(const s , t : String; Count : Integer) : Integer;
|
|
var
|
|
i, h, Last : Integer;
|
|
u : String;
|
|
begin
|
|
u := t;
|
|
if Count > 0 then begin
|
|
Result := Length(t);
|
|
for i := 1 to Count do begin
|
|
h := Pos(s, u);
|
|
if h > 0 then
|
|
u := Copy(u, h + 1, Length(u))
|
|
else begin
|
|
u := '';
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
Result := Result - Length(u);
|
|
end
|
|
else if Count < 0 then begin
|
|
Last := 0;
|
|
for i := Length(t) downto 1 do begin
|
|
u := Copy(t, i, Length(t));
|
|
h := Pos(s, u);
|
|
if (h <> 0) and ((h + i) <> Last) then begin
|
|
Last := h + i - 1;
|
|
Inc(count);
|
|
if Count = 0 then
|
|
break;
|
|
end;
|
|
end;
|
|
if Count = 0 then
|
|
Result := Last
|
|
else
|
|
Result := 0;
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
{ Syntax of an URL: protocol://[user[:password]@]server[:port]/path }
|
|
{Thanx to François PIETTE}
|
|
procedure ParseURL(
|
|
const url : String;
|
|
var Proto, User, Pass, Host, Port, Path : String);
|
|
var
|
|
p, q : Integer;
|
|
s : String;
|
|
begin
|
|
proto := '';
|
|
User := '';
|
|
Pass := '';
|
|
Host := '';
|
|
Port := '';
|
|
Path := '';
|
|
|
|
if Length(url) < 1 then
|
|
Exit;
|
|
|
|
p := pos('://',url);
|
|
if p = 0 then begin
|
|
if (url[1] = '/') then begin
|
|
{ Relative path without protocol specified }
|
|
proto := 'http';
|
|
p := 1;
|
|
if (Length(url) > 1) and (url[2] <> '/') then begin
|
|
{ Relative path }
|
|
Path := Copy(url, 1, Length(url));
|
|
Exit;
|
|
end;
|
|
end
|
|
else if lowercase(Copy(url, 1, 5)) = 'http:' then begin
|
|
proto := 'http';
|
|
p := 6;
|
|
if (Length(url) > 6) and (url[7] <> '/') then begin
|
|
{ Relative path }
|
|
Path := Copy(url, 6, Length(url));
|
|
Exit;
|
|
end;
|
|
end
|
|
else if lowercase(Copy(url, 1, 7)) = 'mailto:' then begin
|
|
proto := 'mailto';
|
|
p := pos(':', url);
|
|
end;
|
|
end
|
|
else begin
|
|
proto := Copy(url, 1, p - 1);
|
|
inc(p, 2);
|
|
end;
|
|
s := Copy(url, p + 1, Length(url));
|
|
|
|
p := pos('/', s);
|
|
if p = 0 then
|
|
p := Length(s) + 1;
|
|
Path := Copy(s, p, Length(s));
|
|
s := Copy(s, 1, p-1);
|
|
|
|
p := Posn(':', s, -1);
|
|
if p > Length(s) then
|
|
p := 0;
|
|
q := Posn('@', s, -1);
|
|
if q > Length(s) then
|
|
q := 0;
|
|
if (p = 0) and (q = 0) then begin { no user, password or port }
|
|
Host := s;
|
|
Exit;
|
|
end
|
|
else if q < p then begin { a port given }
|
|
Port := Copy(s, p + 1, Length(s));
|
|
Host := Copy(s, q + 1, p - q - 1);
|
|
if q = 0 then
|
|
Exit; { no user, password }
|
|
s := Copy(s, 1, q - 1);
|
|
end
|
|
else begin
|
|
Host := Copy(s, q + 1, Length(s));
|
|
s := Copy(s, 1, q - 1);
|
|
end;
|
|
p := pos(':', s);
|
|
if p = 0 then
|
|
User := s
|
|
else begin
|
|
User := Copy(s, 1, p - 1);
|
|
Pass := Copy(s, p + 1, Length(s));
|
|
end;
|
|
end;
|
|
|
|
function DosToHTML(FName: string): string;
|
|
{convert a Dos style filename to one for HTML. Does not add the file:///}
|
|
var
|
|
Colon: integer;
|
|
|
|
procedure Replace(Old, New: char);
|
|
var
|
|
I: integer;
|
|
begin
|
|
I := Pos(Old, FName);
|
|
while I > 0 do
|
|
begin
|
|
FName[I] := New;
|
|
I := Pos(Old, FName);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Colon := Pos('://', FName);
|
|
Replace(':', '|');
|
|
Replace('\', '/');
|
|
if Colon > 0 then
|
|
FName[Colon] := ':'; {return it to a colon}
|
|
Result := FName;
|
|
end;
|
|
|
|
function DosToHtmlNoSharp(FName: string): string;
|
|
{convert a Dos style filename to one for HTML. Does not add the file:///.
|
|
use where "#" is not being used as a local position but rather is part of filename.}
|
|
var
|
|
I: integer;
|
|
begin
|
|
I := Pos('#', FName);
|
|
while I > 0 do
|
|
begin
|
|
Delete(FName, I, 1);
|
|
Insert('%23', FName, I);
|
|
I := Pos('#', FName);
|
|
end;
|
|
Result := FName;
|
|
end;
|
|
|
|
end.
|