| @@ -0,0 +1,250 @@ | |||
| unit uutlSScanf; | |||
| {$mode objfpc}{$H+} | |||
| interface | |||
| uses | |||
| Classes, SysUtils; | |||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer): Integer; | |||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer; const FormatSettings: TFormatSettings): Integer; | |||
| implementation | |||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer): Integer; | |||
| begin | |||
| Result:= utlSScanf(s, fmt, Pointers, DefaultFormatSettings); | |||
| end; | |||
| { | |||
| 0 on success (perfect match) | |||
| +N if fmt was consumed without error, but there are characters left in s. First unparsed character is N | |||
| -N if there was an error consuming pattern character N | |||
| %s: string, must be terminated by a fixed string in fmt | |||
| %d: integer | |||
| %D: int64 | |||
| %f: double, supports scientific notation | |||
| %P: hex PtrUInt (fixed length) | |||
| %x: hex integer | |||
| %X: hex int64 | |||
| } | |||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer; const FormatSettings: TFormatSettings): Integer; | |||
| var | |||
| pt, fpos, tokfpos, spos: integer; | |||
| token, limit: string; | |||
| limitChar: Char; | |||
| success: Boolean; | |||
| function nextToken(advance: boolean): string; | |||
| var | |||
| oldfpos: integer; | |||
| begin | |||
| Result:= ''; | |||
| oldfpos:= fpos; | |||
| try | |||
| if fpos > Length(fmt) then | |||
| Exit; | |||
| Result:= fmt[fpos]; | |||
| inc(fpos); | |||
| if Result = '%' then begin | |||
| if fpos > Length(fmt) then | |||
| Exit; // incomplete token, treat as literal % | |||
| Result += fmt[fpos]; | |||
| inc(fpos); | |||
| if Result = '%%' then | |||
| Result:= '%'; | |||
| end; | |||
| finally | |||
| if not advance then | |||
| fpos:= oldfpos | |||
| else | |||
| tokfpos:= oldfpos; | |||
| end; | |||
| end; | |||
| function GetString: boolean; | |||
| var | |||
| tmp: string; | |||
| begin | |||
| tmp:= ''; | |||
| Result:= false; | |||
| if limitChar = #0 then begin | |||
| tmp:= Copy(s, spos, MaxInt); | |||
| inc(spos, length(tmp)); | |||
| Result:= true; | |||
| end else begin | |||
| while spos <= Length(s) do begin | |||
| if s[spos] = limitChar then begin | |||
| dec(spos); // consume again | |||
| Result:= true; | |||
| break; | |||
| end; | |||
| tmp:= tmp + s[spos]; | |||
| inc(spos); | |||
| end; | |||
| end; | |||
| PString(Pointers[pt])^:= tmp; | |||
| end; | |||
| function GetInteger(i64: boolean): Boolean; | |||
| var | |||
| tmp: string; | |||
| v: int64; | |||
| begin | |||
| tmp:= ''; | |||
| Result:= false; | |||
| while spos <= Length(s) do begin | |||
| if s[spos] = limitChar then begin | |||
| dec(spos); // consume again | |||
| break; | |||
| end; | |||
| tmp:= tmp + s[spos]; | |||
| if not TryStrToInt64(tmp, v) then begin | |||
| dec(spos); | |||
| SetLength(tmp, Length(tmp) - 1); | |||
| Break; | |||
| end; | |||
| inc(spos); | |||
| end; | |||
| if TryStrToInt64(tmp, v) then begin | |||
| if i64 then | |||
| PInt64(Pointers[pt])^:= v | |||
| else | |||
| PInteger(Pointers[pt])^:= v; | |||
| Result:= true; | |||
| end; | |||
| end; | |||
| function GetFloat: Boolean; | |||
| var | |||
| tmp: string; | |||
| v: double; | |||
| lastChar: char; | |||
| firstFailure, fflen: Integer; | |||
| begin | |||
| tmp:= ''; | |||
| Result:= false; | |||
| firstFailure:= 0; | |||
| while spos <= Length(s) do begin | |||
| if s[spos] = limitChar then begin | |||
| dec(spos); // consume again | |||
| break; | |||
| end; | |||
| tmp:= tmp + s[spos]; | |||
| if not TryStrToFloat(tmp, v, FormatSettings) then begin | |||
| lastChar:= tmp[length(tmp)]; | |||
| if (firstFailure = 0) and | |||
| ((lastChar = FormatSettings.ThousandSeparator) or (lastChar in ['-','E'])) then begin | |||
| firstFailure:= spos; | |||
| fflen:= Length(tmp); | |||
| end else begin | |||
| Break; | |||
| end; | |||
| end else | |||
| firstFailure:= 0; | |||
| inc(spos); | |||
| end; | |||
| if firstFailure > 0 then begin | |||
| spos:= firstFailure; | |||
| dec(spos); | |||
| SetLength(tmp, fflen - 1); | |||
| end; | |||
| if TryStrToFloat(tmp, v, FormatSettings) then begin | |||
| PDouble(Pointers[pt])^:= v; | |||
| Result:= true; | |||
| end; | |||
| end; | |||
| function GetHex(mode: byte): Boolean; | |||
| const | |||
| ptrlen = Sizeof(PtrUInt) * 2; | |||
| var | |||
| tmp: string; | |||
| v: int64; | |||
| begin | |||
| tmp:= '$'; | |||
| Result:= false; | |||
| if mode = 0 then begin | |||
| tmp += Copy(s, spos, ptrlen); | |||
| inc(spos, length(tmp)); | |||
| if Length(tmp) <> ptrlen+1 then | |||
| Exit; | |||
| end else | |||
| while spos <= Length(s) do begin | |||
| if s[spos] = limitChar then begin | |||
| dec(spos); // consume again | |||
| break; | |||
| end; | |||
| tmp:= tmp + s[spos]; | |||
| if not TryStrToInt64(tmp, v) then begin | |||
| dec(spos); | |||
| SetLength(tmp, Length(tmp) - 1); | |||
| Break; | |||
| end; | |||
| inc(spos); | |||
| end; | |||
| if TryStrToInt64(tmp, v) then begin | |||
| case mode of | |||
| 0: PPtrUInt(Pointers[pt])^:= v; | |||
| 1: PInteger(Pointers[pt])^:= v; | |||
| 2: PInt64(Pointers[pt])^:= v; | |||
| end; | |||
| Result:= true; | |||
| end; | |||
| end; | |||
| begin | |||
| pt:= 0; | |||
| fpos:= 1; | |||
| spos:= 1; | |||
| while fpos <= length(fmt) do begin | |||
| if spos > Length(s) then | |||
| Exit(-fpos); | |||
| token:= nextToken(true); | |||
| if token = '' then | |||
| Exit(-tokfpos); | |||
| if Length(token) = 1 then begin | |||
| if token <> s[spos] then | |||
| Exit(-tokfpos); | |||
| end else begin | |||
| limit:= nextToken(false); | |||
| if Length(limit) = 1 then | |||
| limitChar:= limit[1] | |||
| else | |||
| limitChar:= #0; | |||
| case token[2] of | |||
| 's': success:= GetString; | |||
| 'd': success:= GetInteger(false); | |||
| 'D': success:= GetInteger(True); | |||
| 'f': success:= GetFloat; | |||
| 'P': success:= GetHex(0); | |||
| 'x': success:= GetHex(1); | |||
| 'X': success:= GetHex(2); | |||
| else | |||
| Exit(-tokfpos); | |||
| end; | |||
| if not success then | |||
| Exit(-tokfpos); | |||
| inc(pt); | |||
| end; | |||
| inc(spos); | |||
| end; | |||
| if spos <= Length(s) then | |||
| Exit(spos) | |||
| else | |||
| Exit(0); | |||
| end; | |||
| end. | |||