quote.inc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. function ScriptUnquote(const S: string): string;
  3. var errors: TInterpretationErrors;
  4. begin
  5. errors := TryScriptUnquote(s,result);
  6. if errors <> [] then
  7. raise exception.create('Invalid quoted string (error '+inttostr(integer(errors))+')');
  8. end;
  9. function UnescapeString(const S: string): string;
  10. const HexDigit = ['0'..'9','a'..'f','A'..'F'];
  11. OctDigit = ['0'..'7'];
  12. var
  13. outputpos: integer;
  14. procedure put(c: char);
  15. begin
  16. if outputpos > length(result) then
  17. setlength(result, length(result)*2+1);
  18. result[outputpos] := c;
  19. inc(outputpos);
  20. end;
  21. procedure putStr(s: string);
  22. var
  23. j: Integer;
  24. begin
  25. for j := 1 to length(s) do
  26. put(s[j]);
  27. end;
  28. function CheckHex(AFrom,ATo: integer): boolean;
  29. var
  30. j: Integer;
  31. begin
  32. if ATo > length(s) then exit(false);
  33. for j := AFrom to ATo do
  34. if not (s[j] in HexDigit) then exit(false);
  35. result := true;
  36. end;
  37. function CheckOct(AFrom,ATo: integer): boolean;
  38. var
  39. j: Integer;
  40. begin
  41. if ATo > length(s) then exit(false);
  42. for j := AFrom to ATo do
  43. if not (s[j] in OctDigit) then exit(false);
  44. result := true;
  45. end;
  46. function OctToInt(s: string): integer;
  47. var
  48. j: Integer;
  49. begin
  50. result := 0;
  51. for j := 1 to length(s) do
  52. result := (result shl 3)+ord(s[j])-ord('0');
  53. end;
  54. var
  55. i: Integer;
  56. escaping: boolean;
  57. begin
  58. setlength(result, length(s));
  59. escaping := false;
  60. outputpos := 1;
  61. i := 1;
  62. while i <= length(s) do
  63. begin
  64. if escaping then
  65. begin
  66. case s[i] of
  67. '\','''','"': put(s[i]);
  68. 'a': put(#7);
  69. 'b': put(#8);
  70. 'f': put(#12);
  71. 'n': put(#10);
  72. 'r': put(#13);
  73. 't': put(#9);
  74. 'v': put(#11);
  75. '0'..'7': if CheckOct(i+1,i+3) then
  76. begin
  77. putstr(UnicodeCharToUTF8(OctToInt(copy(s,i+1,2))));
  78. inc(i,3);
  79. end else putstr('\'+s[i]);
  80. 'x': if CheckHex(i+1,i+2) then
  81. begin
  82. putstr(UnicodeCharToUTF8(StrToInt('$'+copy(s,i+1,2))));
  83. inc(i,2);
  84. end else putstr('\'+s[i]);
  85. 'u': if CheckHex(i+1,i+4) then
  86. begin
  87. putstr(UnicodeCharToUTF8(StrToInt('$'+copy(s,i+1,4))));
  88. inc(i,4);
  89. end else putstr('\'+s[i]);
  90. 'U': if CheckHex(i+1,i+8) then
  91. begin
  92. putstr(UnicodeCharToUTF8(StrToInt('$'+copy(s,i+1,8))));
  93. inc(i,8);
  94. end else putstr('\'+s[i]);
  95. else putstr('\'+s[i]);
  96. end;
  97. escaping := false;
  98. end else
  99. if s[i] = '\' then escaping := true
  100. else put(s[i]);
  101. inc(i);
  102. end;
  103. setlength(result, outputpos-1);
  104. end;
  105. function TryScriptUnquote(const S: String; out unquotedS: string): TInterpretationErrors;
  106. var curPos,quoteStart,idStart: integer; idStr, charCodeStr: string;
  107. charFuncStep: (fsNone, fsWaitOpenBracket, fsCharCodeParam, fsWaitCloseBraket);
  108. escaping: Boolean;
  109. procedure AppendChar;
  110. var errPos: integer;
  111. charValue: integer;
  112. begin
  113. val(charCodeStr,charValue,errPos);
  114. if (errPos = 0) and (charValue >= 0) and (charValue < 128) then
  115. unquotedS:=unquotedS+chr(charValue)
  116. else
  117. result += [ieInvalidNumber];
  118. end;
  119. begin
  120. unquotedS:= '';
  121. curPos := 1;
  122. charFuncStep:= fsNone;
  123. charCodeStr := ''; //init
  124. result := [];
  125. while curPos <= length(s) do
  126. begin
  127. if s[curPos] in[' ',#9..#13,'+','&'] then
  128. begin
  129. if (charFuncStep = fsCharCodeParam) and (charCodeStr <> '') then charFuncStep:= fsWaitCloseBraket;
  130. //ignore whitespaces or concatenation operators
  131. end else
  132. if charFuncStep <> fsNone then
  133. begin
  134. //loose interpretation
  135. if (charFuncStep = fsWaitOpenBracket) and (s[CurPos] <> '(') then
  136. begin
  137. result += [ieOpeningBracketNotFound];
  138. charFuncStep:= fsCharCodeParam;
  139. end else
  140. if (charFuncStep = fsWaitCloseBraket) and (s[CurPos] <> ')') then
  141. begin
  142. result += [ieClosingBracketNotFound];
  143. AppendChar;
  144. charFuncStep:= fsNone;
  145. end;
  146. //strict interpretation
  147. if (charFuncStep = fsWaitOpenBracket) and (s[CurPos] = '(') then
  148. charFuncStep:= fsCharCodeParam
  149. else if (charFuncStep = fsWaitCloseBraket) and (s[CurPos] = ')') then
  150. begin
  151. AppendChar;
  152. charFuncStep:= fsNone;
  153. end else
  154. if charFuncStep = fsCharCodeParam then
  155. begin
  156. if s[CurPos] = ')' then
  157. begin
  158. AppendChar;
  159. charFuncStep:= fsNone;
  160. end else
  161. if not (s[CurPos] in['0'..'9']) then
  162. begin
  163. result += [ieUnexpectedChar];
  164. AppendChar;
  165. charFuncStep:= fsNone;
  166. end else
  167. charCodeStr := charCodeStr+s[CurPos];
  168. end;
  169. end else
  170. if s[curPos] in StringDelimiters then
  171. begin
  172. quoteStart := curPos;
  173. escaping := false;
  174. inc(curPos);
  175. while true do
  176. begin
  177. if curPos <= length(s) then
  178. begin
  179. if not escaping then
  180. begin
  181. if s[curPos]=EscapePrefix then
  182. escaping := true
  183. else
  184. if s[curPos]=s[quoteStart] then
  185. begin
  186. unquotedS:= unquotedS+UnescapeString(copy(s,quoteStart+1,curPos-quoteStart-1));
  187. inc(curPos);
  188. break;
  189. end;
  190. end else
  191. escaping := false;
  192. inc(curPos);
  193. end else
  194. begin
  195. result += [ieEndingQuoteNotFound];
  196. break;
  197. end;
  198. end;
  199. dec(curPos);
  200. end else
  201. if s[curPos] in IdentifierCharStart then
  202. begin
  203. idStart := curPos;
  204. while (curPos+1 <= length(s)) and (s[curPos+1] in IdentifierCharMiddle) do inc(curPos);
  205. idStr := copy(s,idStart,curPos-idStart+1);
  206. if (CompareText(idStr,CharToken1)=0) or (CompareText(idStr,CharToken2)=0) then
  207. begin
  208. charFuncStep:= fsWaitOpenBracket;
  209. charCodeStr := '';
  210. end else
  211. result += [ieConstantExpressionExpected];
  212. end else
  213. result := [ieUnexpectedChar];
  214. inc(curPos);
  215. end;
  216. end;
  217. function ScriptQuote(const S: string): string;
  218. const
  219. StringDelimiter = StringDelimiter1;
  220. EscapeChars = [#0,#7..#13,#26,#27,'\',StringDelimiter];
  221. var i, j, count: integer;
  222. procedure FlushChars;
  223. var NbFlush: integer;
  224. begin
  225. NbFlush := i - j - 1;
  226. if NbFlush <= 0 then exit;
  227. result := result + copy(S, 1 + j, NbFlush);
  228. j := i;
  229. end;
  230. begin
  231. result := StringDelimiter;
  232. count := length(s);
  233. i := 0;
  234. j := 0;
  235. while i < count do
  236. begin
  237. i := i + 1;
  238. if s[i] in EscapeChars then
  239. begin
  240. FlushChars;
  241. case s[i] of
  242. #7: result += '\a';
  243. #8: result += '\b';
  244. #9: result += '\t';
  245. #10: result += '\n';
  246. #11: result += '\v';
  247. #12: result += '\f';
  248. #13: result += '\r';
  249. ' '..#127: result += '\'+s[i];
  250. else result += '\x'+IntToHex(ord(s[i]),2);
  251. end;
  252. j := i;
  253. end;
  254. end;
  255. if i <> j then
  256. result := result + copy(S, 1 + j, i - j);
  257. result += StringDelimiter;
  258. end;