strlib.inc 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719
  1. #include <a_samp>
  2. #if !defined STRLIB_BUFFER_SIZE
  3. #define STRLIB_BUFFER_SIZE 2048
  4. #endif
  5. #if !defined STRLIB_RETURN_SIZE
  6. #define STRLIB_RETURN_SIZE 128
  7. #endif
  8. #if !defined STRLIB_USE_FORMATEX
  9. #if defined __fmt_funcinc
  10. #if !defined FormatSpecifier
  11. #error Please include formatex before strlib.
  12. #endif
  13. #define STRLIB_USE_FORMATEX true
  14. #else
  15. #define STRLIB_USE_FORMATEX false
  16. #endif
  17. #endif
  18. // Used in strtrim (deprecated)
  19. enum trim_edges {
  20. trim_left = 1,
  21. trim_right = 2,
  22. trim_both = trim_left | trim_right
  23. };
  24. // Used in strtrim and strpad
  25. enum string_edges {
  26. edge_left = 1,
  27. edge_right = 2,
  28. edge_both = edge_left | edge_right
  29. };
  30. /*
  31. * Returns a formatted string.
  32. *
  33. * Parameters:
  34. * fmat[] - The format string.
  35. * ... - The format variables.
  36. *
  37. * Returns:
  38. * The formatted string.
  39. */
  40. forward sprintf(const fmat[], {Float, _}:...);
  41. /*
  42. * Get the first character of a string
  43. *
  44. * Parameters:
  45. * string[] - The string.
  46. *
  47. * Returns:
  48. * The first character of the string.
  49. */
  50. forward strgetfirstc(const string[]);
  51. /*
  52. * Get a character from a specific index in a string.
  53. *
  54. * Parameters:
  55. * string[] - The string.
  56. * index - The position in the string.
  57. *
  58. * Returns:
  59. * The character at that index, or '\0' if out of range.
  60. */
  61. forward strgetc(const string[], index);
  62. /*
  63. * Get the size of a string.
  64. *
  65. * Parameters:
  66. * string[] - The string.
  67. *
  68. * Returns:
  69. * The size of the string, in bytes.
  70. */
  71. forward strsize(const string[]);
  72. /*
  73. * Find out if a string is empty.
  74. *
  75. * Parameters:
  76. * string[] - The string.
  77. *
  78. * Returns:
  79. * True if empty, otherwise false.
  80. */
  81. forward bool:isempty(const string[]);
  82. /*
  83. * Compare two strings.
  84. *
  85. * Parameters:
  86. * str1[] - The first string.
  87. * str2[] - The second string.
  88. * ignorecase - Whether to compare them in a case-insensitive manner.
  89. *
  90. * Returns:
  91. * True if equal, otherwise false.
  92. */
  93. forward bool:isequal(const str1[], const str2[], bool:ignorecase = false);
  94. /*
  95. * Split a string by a given delimiter.
  96. *
  97. * Parameters:
  98. * output[][] - A multi-dimensional array that will be filled with substrings.
  99. * input[] - The input string to split.
  100. * delimiter[] - The delimiter to split by. Defaults to ",".
  101. * limit - The max. no. substrings.
  102. * trim - Whether to trim the substrings from whitespace. Defaults to true.
  103. * ignorecase - Whether the search for "delimiter" should be case-insensitive.
  104. * size1 - The size of the 1st dimension of output (otput[this][]). Defaults to sizeof(output).
  105. * size2 - The size of the 2nd dimension of output (otput[][this]). Defaults to sizeof(output[]).
  106. *
  107. * Returns:
  108. * The number of substrings that were copied into the array.
  109. */
  110. forward strexplode(output[][], const input[], const delimiter[] = !",", limit = cellmax, bool:trim = true, bool:ignorecase = false, size1 = sizeof(output), size2 = sizeof(output[]));
  111. /*
  112. * Glue together strings into one.
  113. *
  114. * Parameters:
  115. * glue[] - The string that will be between all other strings.
  116. * output[] - The output string.
  117. * maxlength - The size of "output". Defaults to sizeof(output).
  118. * ...[] - Strings to glue together.
  119. *
  120. * Returns:
  121. * Nothing
  122. */
  123. forward strimplode(const glue[], output[], maxlength = sizeof(output), ...);
  124. /*
  125. * Replace occurrences of the search string with the replacement string.
  126. *
  127. * Parameters:
  128. * string[] - The string to perform the replacing in.
  129. * search[] - The string to look for.
  130. * replacement[] - The string to put instead of "search".
  131. * ignorecase - Whether the search for "search" should be case-insensitive. Defaults to false.
  132. * pos - The position to start at. Defaults to 0 (the beginning).
  133. * limit - Limit the number of replacements. Defaults to -1 (no limit).
  134. * maxlength - The size of "string". Defaults to sizeof(string).
  135. *
  136. * Returns:
  137. * The number of replacements that were made.
  138. */
  139. forward strreplace(string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1, maxlength = sizeof(string));
  140. /*
  141. * Trim whitespace or a specific group of characters from a string.
  142. *
  143. * Parameters:
  144. * string[] - The string to trim.
  145. * chars[] - A string with characters to trim, or all whitespace if empty. Default is all whitespace.
  146. * edge - The edge(s) to trim (edge_left/edge_right/edge_both). Default is edge_both.
  147. *
  148. * Returns:
  149. * Nothing
  150. */
  151. forward strtrim(string[], const chars[] = !"", string_edges:edge = edge_both);
  152. /*
  153. * Pad edge(s) of a string with spaces.
  154. *
  155. * Parameters:
  156. * string[] - The string to pad.
  157. * length - The new length of the string.
  158. * substr[] - The substring to pad with. Defaults to a space (" ").
  159. * edge - The edge(s) to pad (edge_left/edge_right/edge_both). Default is edge_both.
  160. * trim_first - Whether to trim the string before padding.
  161. * trim_chars[] - The chars to trim, defaults is all whitespace.
  162. * maxlength - The size of "string". Defaults to sizeof(string).
  163. * input - Used internally.
  164. */
  165. forward strpad(string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"", maxlength = sizeof(string), const input[] = !"");
  166. /*
  167. * Wrap a string inside two other strings.
  168. *
  169. * Parameters:
  170. * left[] - The string on the left side.
  171. * string[] - The middle string that will be modified.
  172. * right[] - The string on the right side.
  173. * maxlength - The size of "string". Defaults to sizeof(string).
  174. */
  175. forward strwrap(const left[], string[], const right[], maxlength = sizeof(string));
  176. /*
  177. * Count substrings.
  178. *
  179. * Parameters:
  180. * string[] - The string to search inside.
  181. * sub[] - The string to search for.
  182. * ignorecase - Whether the search should be case-insensitive.
  183. * count_overlapped - Whether to count overlapping strings ("abcabc" in "abcabcabc" will count 2 instead of 1).
  184. *
  185. * Returns:
  186. * The number of occurrences of "sub" in "string".
  187. */
  188. forward strcount(const string[], const sub[], bool:ignorecase = false, bool:count_overlapped = false);
  189. /*
  190. * Read a string from a PAWN string literal.
  191. *
  192. * Parameters:
  193. * output[] - The variable to save into.
  194. * input[] - The string literal.
  195. * pos - The position in "input" to start reading from. Will be modified to the end of the literal.
  196. * maxlength - The size of "output". Defaults to sizeof(output).
  197. *
  198. * Returns:
  199. * true on success, false on error.
  200. */
  201. forward bool:strfromliteral(output[], const input[], &pos = 0, maxlength = sizeof(output));
  202. /*
  203. * Build a PAWN string literal from a given string.
  204. *
  205. * Parameters:
  206. * output[] - The variable to save into.
  207. * substrings[] - The string to build from.
  208. * maxlength - The size of "output". Defaults to sizeof(output).
  209. *
  210. * Returns:
  211. * Nothing
  212. */
  213. forward strtoliteral(output[], const input[], maxlength = sizeof(output), bool:paranoid = true);
  214. /*
  215. * Convert an array to a string.
  216. *
  217. * Example: {0x1122, 0x5566} becomes "0000112200005566".
  218. *
  219. * Parameters:
  220. * output[] - The variable to save into.
  221. * input[] - The array to build from.
  222. * inputlength - The size of "input". Defaults to sizeof(input).
  223. * maxlength - The size of "output". Defaults to sizeof(output).
  224. *
  225. * Returns:
  226. * Nothing
  227. */
  228. forward strfrombin(output[], const input[], inputlength = sizeof(input), maxlength = sizeof(output));
  229. /*
  230. * Convert a string to an array.
  231. *
  232. * Example: "0000112200005566" becomes {0x1122, 0x5566}.
  233. *
  234. * Parameters:
  235. * output[] - The variable to save into.
  236. * input[] - The array to build from.
  237. * maxlength - The size of "output". Defaults to sizeof(output).
  238. *
  239. * Returns:
  240. * The length of the output, in cells.
  241. */
  242. forward strtobin(output[], const input[], maxlength = sizeof(output));
  243. /*
  244. * Concatenate one string with a part of another.
  245. *
  246. * Parameters:
  247. * dest[] - The variable to concatenate the other part with.
  248. * source[] - The string to extract from.
  249. * start - The start offset, defaults to 0.
  250. * end - The start offset, defaults to end of string.
  251. * maxlength - The size of "dest". Defaults to sizeof(dest).
  252. */
  253. forward strcatmid(dest[], const source[], start = 0, end = -1, maxlength = sizeof(dest));
  254. /*
  255. * UTF-8 encode a string. Characters above 127 will be encoded into
  256. * two or more characters.
  257. *
  258. * Parameters:
  259. * dest[] - The output variable.
  260. * source[] - The string to encode.
  261. * maxlength - The size of "dest". Defaults to sizeof(dest).
  262. */
  263. forward utf8encode(dest[], const source[], maxlength = sizeof(dest));
  264. /*
  265. * UTF-8 decode a string. UTF-8 characters will be collapsed into single
  266. * characters in the array.
  267. *
  268. * Parameters:
  269. * dest[] - The output variable.
  270. * source[] - The string to encode.
  271. * maxlength - The size of "dest". Defaults to sizeof(dest).
  272. */
  273. forward utf8decode(dest[], const source[], maxlength = sizeof(dest));
  274. /*
  275. * Decode an encoded URL.
  276. *
  277. * Parameters:
  278. * output[] - The output variable.
  279. * input[] - The string to decode.
  280. * maxlength - The size of "output". Defaults to sizeof(output).
  281. */
  282. forward strurldecode(output[], const input[], maxlength = sizeof(output));
  283. /*
  284. * URL encode a string.
  285. *
  286. * Parameters:
  287. * output[] - The output variable.
  288. * input[] - The string to encode.
  289. * maxlength - The size of "output". Defaults to sizeof(output).
  290. * pack - Whether to pack the output. Defaults to false.
  291. */
  292. forward strurlencode(output[], const input[], maxlength = sizeof(output), bool:pack = false);
  293. // Same as above, but output is returned
  294. forward ret_strcatmid(const string[], const source[], start = 0, end = -1);
  295. forward ret_strfrombin(const input[], inputlength = sizeof(input));
  296. forward ret_strimplode(const glue[], ...);
  297. forward ret_strreplace(const string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1);
  298. forward ret_strfromliteral(const input[], &pos = 0);
  299. forward ret_strtoliteral(const input[], bool:paranoid = true);
  300. forward ret_strtrim(const string[], const chars[] = !"", string_edges:edge = edge_both);
  301. forward ret_strpad(const string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"");
  302. forward ret_strwrap(const left[], const string[], const right[]);
  303. forward ret_strurldecode(const input[]);
  304. forward ret_strurlencode(const input[], bool:pack = false);
  305. forward ret_utf8encode(const input[]);
  306. forward ret_utf8decode(const input[]);
  307. // Return from native functions
  308. forward ret_strpack(const source[]);
  309. forward ret_strunpack(const source[]);
  310. forward ret_strcat(const string1[], const string2[]);
  311. forward ret_strmid(const source[], start, end);
  312. forward ret_strins(const string[], const substr[], pos, maxlength = sizeof(string));
  313. forward ret_strdel(const string[], start, end);
  314. forward ret_valstr(value, bool:pack = false);
  315. forward ret_GetPlayerName(playerid, bool:pack = false);
  316. stock
  317. // Used throughout the library
  318. g_StrlibBuffer[2048]
  319. ;
  320. // Workaround for compiler bug
  321. forward _strlib_funcinc();
  322. public _strlib_funcinc() {
  323. new temp[1];
  324. format(!"", 0, !"");
  325. strcat(temp, temp);
  326. strpack(temp, temp);
  327. strunpack(temp, temp);
  328. }
  329. // Internal functions
  330. static stock RedirectArgument(arg, ...) {
  331. #emit LOAD.S.pri 0
  332. #emit ADD.C 12
  333. #emit LOAD.S.alt arg
  334. #emit SHL.C.alt 2
  335. #emit ADD
  336. #emit MOVE.alt
  337. #emit LOAD.S.pri 16
  338. #emit STOR.I
  339. }
  340. static stock CopyArgumentToHeap(arg, bool:pack = false, const argptr[] = "") {
  341. new arg_address, address;
  342. #emit LOAD.S.pri 0
  343. #emit ADD.C 12
  344. #emit LOAD.S.alt arg
  345. #emit SHL.C.alt 2
  346. #emit ADD
  347. #emit LOAD.I
  348. #emit STOR.S.pri arg_address
  349. #emit STOR.S.pri argptr
  350. if (pack) {
  351. new bytes = ((strlen(argptr) + 1 + 3) / 4) * 4;
  352. #emit LCTRL 2
  353. #emit STOR.S.pri address
  354. #emit LOAD.S.alt bytes
  355. #emit ADD
  356. #emit SCTRL 2
  357. //strpack(dest[], const source[], maxlength = sizeof dest)
  358. #emit LOAD.S.pri bytes
  359. #emit SHR.C.pri 2
  360. #emit PUSH.pri
  361. #emit PUSH.S arg_address
  362. #emit PUSH.S address
  363. #emit PUSH.C 12
  364. #emit SYSREQ.C strpack
  365. #emit STACK 16
  366. } else {
  367. new bytes = (strlen(argptr) + 1) * 4;
  368. #emit LCTRL 2
  369. #emit STOR.S.pri address
  370. #emit LOAD.S.alt bytes
  371. #emit ADD
  372. #emit SCTRL 2
  373. //strunpack(dest[], const source[], maxlength = sizeof dest)
  374. #emit LOAD.S.pri bytes
  375. #emit SHR.C.pri 2
  376. #emit PUSH.pri
  377. #emit PUSH.S arg_address
  378. #emit PUSH.S address
  379. #emit PUSH.C 12
  380. #emit SYSREQ.C strunpack
  381. #emit STACK 16
  382. }
  383. #emit LOAD.S.pri 0
  384. #emit ADD.C 12
  385. #emit LOAD.S.alt arg
  386. #emit SHL.C.alt 2
  387. #emit ADD
  388. #emit MOVE.alt
  389. #emit LOAD.S.pri address
  390. #emit STOR.I
  391. return address;
  392. }
  393. static stock RestoreHeapToAddress(address) {
  394. #emit LOAD.S.pri address
  395. #emit SCTRL 2
  396. }
  397. static stock IsOverlapping(const str1[], size1 = sizeof(str1), const str2[], size2 = sizeof(str2)) {
  398. new addr1, addr2;
  399. if (size1 == -1) {
  400. size1 = strsize(str1);
  401. } else {
  402. size1 *= 4;
  403. }
  404. if (size2 == -1) {
  405. size2 = strsize(str2);
  406. } else {
  407. size2 *= 4;
  408. }
  409. #emit LOAD.S.pri str1
  410. #emit STOR.S.pri addr1
  411. #emit LOAD.S.pri str2
  412. #emit STOR.S.pri addr2
  413. return (addr1 < addr2 + size2) && (addr2 < addr1 + size1);
  414. }
  415. // strlib functions
  416. #define ispacked(%1) \
  417. ((%1)[0] > 255)
  418. stock strgetfirstc(const string[]) {
  419. return ispacked(string) ? string{0} : string[0];
  420. }
  421. stock strgetc(const string[], index) {
  422. if (index < 0)
  423. return '\0';
  424. new len = strlen(string);
  425. if (index >= len)
  426. return '\0';
  427. return ispacked(string) ? string{index} : string[index];
  428. }
  429. stock strsize(const string[]) {
  430. new len = strlen(string);
  431. if (ispacked(string))
  432. return len + 1;
  433. return (len + 1) * 4;
  434. }
  435. stock bool:isempty(const string[]) {
  436. if (ispacked(string))
  437. return string{0} == '\0';
  438. else
  439. return string[0] == '\0';
  440. }
  441. stock bool:isequal(const str1[], const str2[], bool:ignorecase = false) {
  442. new
  443. c1 = (str1[0] > 255) ? str1{0} : str1[0],
  444. c2 = (str2[0] > 255) ? str2{0} : str2[0]
  445. ;
  446. if (!c1 != !c2)
  447. return false;
  448. return !strcmp(str1, str2, ignorecase);
  449. }
  450. stock strimplode(const glue[], output[], maxlength = sizeof(output), ...) {
  451. new args = numargs();
  452. // Null-out "output"
  453. output[0] = '\0';
  454. // Loop the variable arguments (the ones after "maxlength").
  455. for (new arg = 3; arg < args; arg++) {
  456. // If this isn't the first string, append the glue.
  457. if (arg != 3)
  458. strcat(output, glue, maxlength);
  459. // Wrap these in braces or they will be a part of the above if statement (compiler bug)
  460. {
  461. // Get the address of argument no. <arg>
  462. #emit LCTRL 5
  463. #emit ADD.C 12
  464. #emit LOAD.S.alt arg
  465. #emit SHL.C.alt 2
  466. #emit ADD
  467. #emit LOAD.I
  468. // Push the maxlength, arg address, and output address
  469. #emit PUSH.S maxlength
  470. #emit PUSH.pri
  471. #emit PUSH.S output
  472. // Push the argument count
  473. #emit PUSH.C 12
  474. // call strcat
  475. #emit SYSREQ.C strcat
  476. // Restore the stack
  477. #emit STACK 16
  478. }
  479. }
  480. }
  481. stock strexplode(output[][], const input[], const delimiter[] = !",", limit = cellmax, bool:trim = true, bool:ignorecase = false, size1 = sizeof(output), size2 = sizeof(output[])) {
  482. if (!size1 || !size2) {
  483. printf("(strexplode) ERROR: size1 = %d, size2 = %d. Can't be 0.", size1, size2);
  484. return 0;
  485. }
  486. if (isempty(delimiter)) {
  487. print(!"(strexplode) ERROR: delimiter is empty.");
  488. return 0;
  489. }
  490. if (trim) {
  491. new i = -1;
  492. if (ispacked(input)) {
  493. while (input{++i}) {
  494. if (input{i} > ' ') {
  495. i = -1;
  496. break;
  497. }
  498. }
  499. } else {
  500. while (input[++i]) {
  501. if (input[i] > ' ') {
  502. i = -1;
  503. break;
  504. }
  505. }
  506. }
  507. if (i != -1)
  508. return 0;
  509. } else if (isempty(input)) {
  510. return 0;
  511. }
  512. if (limit == 0) {
  513. return 0;
  514. } else if (limit == cellmax) {
  515. limit = 0;
  516. }
  517. new
  518. pos = 0,
  519. next,
  520. bool:packed = ispacked(input),
  521. dlen = strlen(delimiter),
  522. count = 0,
  523. end
  524. ;
  525. while (pos != -1) {
  526. ++count;
  527. if (limit > 0 && count >= limit) {
  528. next = -1;
  529. } else {
  530. next = strfind(input, delimiter, ignorecase, pos);
  531. }
  532. end = (next == -1) ? cellmax : next;
  533. if (trim) {
  534. if (end == cellmax)
  535. end = strlen(input);
  536. if (packed) {
  537. while (0 < input{pos} <= ' ') pos++;
  538. while (end > 0 && input{end - 1} <= ' ') end--;
  539. } else {
  540. while (0 < input[pos] <= ' ') pos++;
  541. while (end > 0 && input[end - 1] <= ' ') end--;
  542. }
  543. }
  544. strmid(output[count - 1], input, pos, end, size2);
  545. if (count >= size1 || next == -1 || (limit < 0 && count >= -limit))
  546. break;
  547. pos = next + dlen;
  548. }
  549. return count;
  550. }
  551. stock strreplace(string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1, maxlength = sizeof(string)) {
  552. // No need to do anything if the limit is 0.
  553. if (limit == 0)
  554. return 0;
  555. new
  556. sublen = strlen(search),
  557. replen = strlen(replacement),
  558. bool:packed = ispacked(string),
  559. maxlen = maxlength,
  560. len = strlen(string),
  561. count = 0
  562. ;
  563. // "maxlen" holds the max string length (not to be confused with "maxlength", which holds the max. array size).
  564. // Since packed strings hold 4 characters per array slot, we multiply "maxlen" by 4.
  565. if (packed)
  566. maxlen *= 4;
  567. // If the length of the substring is 0, we have nothing to look for..
  568. if (!sublen)
  569. return 0;
  570. // In this line we both assign the return value from "strfind" to "pos" then check if it's -1.
  571. while (-1 != (pos = strfind(string, search, ignorecase, pos))) {
  572. // Delete the string we found
  573. strdel(string, pos, pos + sublen);
  574. len -= sublen;
  575. // If there's anything to put as replacement, insert it. Make sure there's enough room first.
  576. if (replen && len + replen < maxlen) {
  577. strins(string, replacement, pos, maxlength);
  578. pos += replen;
  579. len += replen;
  580. }
  581. // Is there a limit of number of replacements, if so, did we break it?
  582. if (limit != -1 && ++count >= limit)
  583. break;
  584. }
  585. return count;
  586. }
  587. stock strtrim(string[], const chars[] = !"", string_edges:edge = edge_both) {
  588. new bool:packed = ispacked(string);
  589. // If "chars" is empty, trim whitespace
  590. if (!strgetfirstc(chars)) {
  591. // Should the left side be trimmed?
  592. if (edge & edge_left) {
  593. new i = 0;
  594. if (packed)
  595. while (0 < string{i} <= ' ') i++;
  596. else
  597. while (0 < string[i] <= ' ') i++;
  598. if (i) {
  599. strdel(string, 0, i);
  600. }
  601. }
  602. // Should the right side be trimmed?
  603. if (edge & edge_right) {
  604. new i = strlen(string);
  605. if (i) {
  606. if (packed) {
  607. while (--i && 0 < string{i} <= ' ') {}
  608. string{i + 1} = '\0';
  609. } else {
  610. while (--i && 0 < string[i] <= ' ') {}
  611. string[i + 1] = '\0';
  612. }
  613. }
  614. }
  615. } else {
  616. // Should the left side be trimmed?
  617. if (edge & edge_left) {
  618. new i = 0, sub[2];
  619. if (packed) {
  620. while ((sub[0] = string{i})) {
  621. if (strfind(chars, sub) == -1)
  622. break;
  623. i++;
  624. }
  625. if (i) {
  626. strdel(string, 0, i);
  627. }
  628. } else {
  629. while ((sub[0] = string[i])) {
  630. if (strfind(chars, sub) == -1)
  631. break;
  632. i++;
  633. }
  634. if (i) strdel(string, 0, i);
  635. }
  636. }
  637. // Should the right side be trimmed?
  638. if (edge & edge_right) {
  639. new i = strlen(string), sub[2];
  640. if (i >= 0) {
  641. if (packed) {
  642. while (i--) {
  643. sub[0] = string{i};
  644. if (strfind(chars, sub) == -1)
  645. break;
  646. }
  647. string{i + 1} = '\0';
  648. } else {
  649. while (i--) {
  650. sub[0] = string[i];
  651. if (strfind(chars, sub) == -1)
  652. break;
  653. }
  654. string[i + 1] = '\0';
  655. }
  656. }
  657. }
  658. }
  659. }
  660. stock strpad(string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"", maxlength = sizeof(string), const input[] = !"") {
  661. if (trim_first) {
  662. strtrim(string, trim_chars, edge);
  663. }
  664. new
  665. heap,
  666. length_left = 0,
  667. length_right = 0,
  668. len = strlen(string),
  669. sublen = strlen(substr),
  670. bool:packed,
  671. bool:subpacked = ispacked(substr)
  672. ;
  673. if (len > length)
  674. return;
  675. else
  676. length -= len;
  677. // Make "input" a pointer to "string"
  678. #emit LOAD.S.pri string
  679. #emit STOR.S.pri input
  680. // Copy "input" to the heap so it won't be linked to "string" anymore.
  681. heap = CopyArgumentToHeap(7);
  682. string[0] = '\0';
  683. len = 0;
  684. switch (edge) {
  685. case edge_left:
  686. length_left = length;
  687. case edge_right:
  688. length_right = length;
  689. default:
  690. length_left = length / 2, length_right = length - length_left;
  691. }
  692. if (length_left) {
  693. while (len < length_left) {
  694. if (subpacked)
  695. strcat(string, substr, length_left * 4);
  696. else
  697. strcat(string, substr, length_left + 1);
  698. len += sublen;
  699. }
  700. if (subpacked)
  701. string{length_left} = 0;
  702. }
  703. strcat(string, input, maxlength);
  704. if (length_right) {
  705. len = strlen(string);
  706. length_right += len;
  707. packed = ispacked(string);
  708. while (len < length_right) {
  709. if (packed)
  710. strcat(string, substr, length_right / 4 + 1);
  711. else
  712. strcat(string, substr, length_right + 1);
  713. len += sublen;
  714. }
  715. if (packed)
  716. string{length_right + 1} = 0;
  717. }
  718. RestoreHeapToAddress(heap);
  719. }
  720. stock strwrap(const left[], string[], const right[], maxlength = sizeof(string)) {
  721. strins(string, left, 0, maxlength);
  722. strcat(string, right, maxlength);
  723. }
  724. stock strcount(const string[], const sub[], bool:ignorecase = false, bool:count_overlapped = false) {
  725. new
  726. increment = count_overlapped ? 1 : strlen(sub),
  727. pos = -increment,
  728. count = 0
  729. ;
  730. while (-1 != (pos = strfind(string, sub, ignorecase, pos + increment)))
  731. count++;
  732. return count;
  733. }
  734. stock bool:strfromliteral(output[], const input[], &pos = 0, maxlength = sizeof(output)) {
  735. new
  736. length = strlen(input),
  737. c,
  738. outlen = 0,
  739. heap = 0
  740. ;
  741. // No need to do anything else.
  742. if (!length)
  743. return true;
  744. if (IsOverlapping(output, maxlength, input, -1))
  745. heap = CopyArgumentToHeap(1);
  746. output[0] = '\0';
  747. if (input[0] == '"')
  748. pos++;
  749. for (;; pos++) {
  750. if (outlen >= maxlength - 1 || pos >= length)
  751. break;
  752. c = input[pos];
  753. switch (c) {
  754. // String ended
  755. case '"': break;
  756. case '\\': {}
  757. default: {
  758. output[outlen++] = c;
  759. continue;
  760. }
  761. }
  762. // String ends with a backslash - invalid.
  763. if (pos == length - 1)
  764. goto return_false;
  765. // We're after a backslash now, let's see what's there.
  766. c = input[++pos];
  767. switch (c) {
  768. case '"',
  769. '\'',
  770. '\\',
  771. '%': output[outlen++] = c;
  772. case 'a': output[outlen++] = '\a';
  773. case 'b': output[outlen++] = '\b';
  774. case 'e': output[outlen++] = '\e';
  775. case 'f': output[outlen++] = '\f';
  776. case 'r': output[outlen++] = '\r';
  777. case 'n': output[outlen++] = '\n';
  778. case 't': output[outlen++] = '\t';
  779. case 'v': output[outlen++] = '\v';
  780. case 'x': {
  781. new val = 0;
  782. // String ends with "\x" - invalid.
  783. if (c == length - 1)
  784. goto return_false;
  785. while ((c = input[pos + 1])) {
  786. if ('a' <= c <= 'f' || 'A' <= c <= 'F') {
  787. val = (val << 4) + (tolower(c) - 'a' + 10);
  788. } else if ('0' <= c <= '9') {
  789. val = (val << 4) + (c - '0');
  790. } else {
  791. break;
  792. }
  793. pos++;
  794. }
  795. if (c == ';')
  796. pos++;
  797. output[outlen++] = val;
  798. }
  799. case '0' .. '9': {
  800. new val = 0;
  801. while ((c = input[pos])) {
  802. if ('0' <= c <= '9') {
  803. val = val * 10 + (c - '0');
  804. } else {
  805. break;
  806. }
  807. pos++;
  808. }
  809. if (c != ';') pos--;
  810. output[outlen++] = val;
  811. }
  812. default: {
  813. goto return_false;
  814. }
  815. }
  816. }
  817. output[outlen] = '\0';
  818. pos++;
  819. new bool:ret = true;
  820. goto return_true;
  821. return_false:
  822. ret = false;
  823. return_true:
  824. if (heap)
  825. RestoreHeapToAddress(heap);
  826. return ret;
  827. }
  828. stock strtoliteral(output[], const input[], maxlength = sizeof(output), bool:paranoid = true) {
  829. new i, c, outlen, heap = 0;
  830. if (IsOverlapping(output, maxlength, input, -1))
  831. heap = CopyArgumentToHeap(1);
  832. output[outlen++] = '"';
  833. for (i = 0; (c = input[i]); i++) {
  834. if (maxlength - outlen <= 3) {
  835. outlen = min(outlen, maxlength - 2);
  836. break;
  837. }
  838. switch (c) {
  839. case ' ', '!', '#' .. '[', ']', '^' .. '~':
  840. output[outlen++] = c;
  841. case '"': strunpack(output[outlen], !"\\\"", 3), outlen += 2;
  842. case '\a': strunpack(output[outlen], !"\\a" , 3), outlen += 2;
  843. case '\b': strunpack(output[outlen], !"\\b" , 3), outlen += 2;
  844. case '\e': strunpack(output[outlen], !"\\e" , 3), outlen += 2;
  845. case '\f': strunpack(output[outlen], !"\\f" , 3), outlen += 2;
  846. case '\r': strunpack(output[outlen], !"\\r" , 3), outlen += 2;
  847. case '\n': strunpack(output[outlen], !"\\n" , 3), outlen += 2;
  848. case '\t': strunpack(output[outlen], !"\\t" , 3), outlen += 2;
  849. case '\v': strunpack(output[outlen], !"\\v" , 3), outlen += 2;
  850. case '\\': strunpack(output[outlen], !"\\\\" , 3), outlen += 2;
  851. default: {
  852. if (!paranoid && 0x80 <= c <= 0xFF) {
  853. output[outlen++] = c;
  854. continue;
  855. }
  856. if (maxlength - outlen <= 8)
  857. break;
  858. format(output[outlen], 7, "\\x%03x;", c);
  859. outlen += 6;
  860. }
  861. }
  862. }
  863. output[outlen++] = '"';
  864. output[outlen] = '\0';
  865. if (heap)
  866. RestoreHeapToAddress(heap);
  867. }
  868. stock strfrombin(output[], const input[], inputlength = sizeof(input), maxlength = sizeof(output)) {
  869. static const hex_chars[] = "0123456789ABCDEF";
  870. new outlen = 0, heap = 0;
  871. if (IsOverlapping(output, maxlength, input, -1))
  872. heap = CopyArgumentToHeap(1);
  873. for (new i = 0; i < inputlength; i++) {
  874. if (maxlength - outlen <= 7) {
  875. outlen = min(outlen, maxlength - 1);
  876. break;
  877. }
  878. new input_cell = input[i];
  879. output[outlen++] = hex_chars[(input_cell ) >>> 28];
  880. output[outlen++] = hex_chars[(input_cell & 0x0F000000) >>> 24];
  881. output[outlen++] = hex_chars[(input_cell & 0x00F00000) >>> 20];
  882. output[outlen++] = hex_chars[(input_cell & 0x000F0000) >>> 16];
  883. output[outlen++] = hex_chars[(input_cell & 0x0000F000) >>> 12];
  884. output[outlen++] = hex_chars[(input_cell & 0x00000F00) >>> 8];
  885. output[outlen++] = hex_chars[(input_cell & 0x000000F0) >>> 4];
  886. output[outlen++] = hex_chars[(input_cell & 0x0000000F) ];
  887. }
  888. output[outlen] = '\0';
  889. if (heap)
  890. RestoreHeapToAddress(heap);
  891. }
  892. stock strtobin(output[], const input[], maxlength = sizeof(output)) {
  893. new len = strlen(input), outlen = 0, heap = 0;
  894. if (IsOverlapping(output, maxlength, input, -1))
  895. heap = CopyArgumentToHeap(1);
  896. for (new i = 0; i < len;) {
  897. if (outlen >= maxlength || i > len - 8) {
  898. break;
  899. }
  900. new c, out = 0;
  901. #define ADD_OUT(%1) \
  902. c = input[i++]; out |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower(c) - 'a' + 10) : (c - '0')) << %1
  903. ADD_OUT(28);
  904. ADD_OUT(24);
  905. ADD_OUT(20);
  906. ADD_OUT(16);
  907. ADD_OUT(12);
  908. ADD_OUT(8);
  909. ADD_OUT(4);
  910. ADD_OUT(0);
  911. #undef ADD_OUT
  912. output[outlen++] = out;
  913. }
  914. if (heap)
  915. RestoreHeapToAddress(heap);
  916. return outlen;
  917. }
  918. stock strurlencode(output[], const input[], maxlength = sizeof(output), bool:pack = false) {
  919. static const hex_chars[] = "0123456789ABCDEF";
  920. new
  921. len = strlen(input),
  922. bool:packed = ispacked(input),
  923. outlen = 0,
  924. heap = 0
  925. ;
  926. if (IsOverlapping(output, maxlength, input, -1))
  927. heap = CopyArgumentToHeap(1, packed);
  928. if (pack)
  929. maxlength *= 4;
  930. for (new i = 0; i < len; i++) {
  931. if (maxlength - outlen <= 1)
  932. break;
  933. new c = packed ? input{i} : input[i];
  934. switch (c) {
  935. case 'a' .. 'z', 'A' .. 'Z', '0' .. '9', '_': {
  936. if (pack)
  937. output{outlen++} = c;
  938. else
  939. output[outlen++] = c;
  940. }
  941. case ' ': {
  942. if (pack)
  943. output{outlen++} = '+';
  944. else
  945. output[outlen++] = '+';
  946. }
  947. default: {
  948. if (maxlength - outlen <= 3)
  949. break;
  950. if (pack) {
  951. output{outlen++} = '%';
  952. output{outlen++} = hex_chars[(c & 0xF0) >>> 4];
  953. output{outlen++} = hex_chars[c & 0x0F];
  954. } else {
  955. output[outlen++] = '%';
  956. output[outlen++] = hex_chars[(c & 0xF0) >>> 4];
  957. output[outlen++] = hex_chars[c & 0x0F];
  958. }
  959. }
  960. }
  961. }
  962. if (pack)
  963. output{outlen} = '\0';
  964. else
  965. output[outlen] = '\0';
  966. if (heap)
  967. RestoreHeapToAddress(heap);
  968. }
  969. stock strurldecode(output[], const input[], maxlength = sizeof(output)) {
  970. new prev_pos = 0, pos = 0, inputlen = strlen(input), len, heap = 0;
  971. if (IsOverlapping(output, maxlength, input, -1))
  972. heap = CopyArgumentToHeap(1);
  973. output[0] = '\0';
  974. while (-1 != (pos = strfind(input, "%", _, pos))) {
  975. static str[2];
  976. new c;
  977. if (prev_pos != pos) {
  978. len = strlen(output);
  979. strcatmid(output, input, prev_pos, pos, maxlength);
  980. strreplace(output, "+", " ", _, len, _, maxlength);
  981. }
  982. if (inputlen < pos + 3)
  983. goto func_end;
  984. str[0] = 0;
  985. c = input[pos + 1]; str[0] |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower(c) - 'a' + 10) : (c - '0')) << 4;
  986. c = input[pos + 2]; str[0] |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower(c) - 'a' + 10) : (c - '0'));
  987. strcat(output, str, maxlength);
  988. prev_pos = (pos += 3);
  989. }
  990. len = strlen(output);
  991. strcatmid(output, input, prev_pos, _, maxlength);
  992. strreplace(output, "+", " ", _, len, _, maxlength);
  993. func_end:
  994. if (heap)
  995. RestoreHeapToAddress(heap);
  996. }
  997. stock strcatmid(dest[], const source[], start = 0, end = -1, maxlength = sizeof(dest)) {
  998. new heap = 0;
  999. if (IsOverlapping(dest, maxlength, source, -1))
  1000. heap = CopyArgumentToHeap(1);
  1001. if (start == 0 && end == -1) {
  1002. strcat(dest, source, maxlength);
  1003. } else {
  1004. if (end == -1)
  1005. end = strlen(source);
  1006. if (ispacked(dest)) {
  1007. new len = strlen(dest);
  1008. if (ispacked(source)) {
  1009. strunpack(g_StrlibBuffer, source);
  1010. strcat(dest, g_StrlibBuffer[start], min(maxlength, (len + end - start) / 4 + 1));
  1011. } else {
  1012. strcat(dest, source[start], min(maxlength, (len + end - start) / 4 + 1));
  1013. }
  1014. dest{len + end - start} = '\0';
  1015. } else {
  1016. if (ispacked(source)) {
  1017. strunpack(g_StrlibBuffer, source);
  1018. strcat(dest, g_StrlibBuffer[start], min(maxlength, strlen(dest) + end - start + 1));
  1019. } else {
  1020. strcat(dest, source[start], min(maxlength, strlen(dest) + end - start + 1));
  1021. }
  1022. }
  1023. }
  1024. if (heap)
  1025. RestoreHeapToAddress(heap);
  1026. }
  1027. stock utf8encode(dest[], const source[], maxlength = sizeof(dest)) {
  1028. new heap = 0;
  1029. if (IsOverlapping(dest, maxlength, source, -1)) {
  1030. heap = CopyArgumentToHeap(1);
  1031. }
  1032. new len = strlen(source);
  1033. new packed = ispacked(source);
  1034. dest[0] = '\0';
  1035. new idx = 0;
  1036. for (new i = 0; i < len; i++) {
  1037. new c = packed ? source{i} : source[i];
  1038. if (c >= 0x80) {
  1039. if (c > 0x4000000) {
  1040. // 6 byte
  1041. dest[idx++] = 0b11111100 | ((c >>> 30) & 0b00000001);
  1042. dest[idx++] = 0b10000000 | ((c >>> 24) & 0b00111111);
  1043. dest[idx++] = 0b10000000 | ((c >>> 18) & 0b00111111);
  1044. dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111);
  1045. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1046. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1047. } else if (c > 0x200000) {
  1048. // 5 byte
  1049. dest[idx++] = 0b11111000 | ((c >>> 24) & 0b00000011);
  1050. dest[idx++] = 0b10000000 | ((c >>> 18) & 0b00111111);
  1051. dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111);
  1052. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1053. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1054. } else if (c > 0x10000) {
  1055. // 4 byte
  1056. dest[idx++] = 0b11110000 | ((c >>> 18) & 0b00000111);
  1057. dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111);
  1058. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1059. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1060. } else if (c > 0x800) {
  1061. // 3 byte
  1062. dest[idx++] = 0b11100000 | ((c >>> 12) & 0b00001111);
  1063. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1064. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1065. } else {
  1066. // 2 byte
  1067. dest[idx++] = 0b11000000 | ((c >>> 6) & 0b00011111);
  1068. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1069. }
  1070. } else if (c > 0) {
  1071. dest[idx++] = c;
  1072. }
  1073. }
  1074. dest[idx++] = '\0';
  1075. if (heap) {
  1076. RestoreHeapToAddress(heap);
  1077. }
  1078. }
  1079. stock utf8decode(dest[], const source[], maxlength = sizeof(dest)) {
  1080. new heap = 0;
  1081. if (IsOverlapping(dest, maxlength, source, -1)) {
  1082. heap = CopyArgumentToHeap(1);
  1083. }
  1084. new len = strlen(source);
  1085. dest[0] = '\0';
  1086. new idx = 0;
  1087. for (new i = 0; i < len; i++) {
  1088. new c = source[i];
  1089. if (c & 0b10000000) {
  1090. if (c & 0b11100000 == 0b11000000) {
  1091. // 2 byte
  1092. if (i + 3 >= len) continue;
  1093. dest[idx++] = (c & 0b00011111) << 6 | (source[++i] & 0b00111111);
  1094. } else if (c & 0b11110000 == 0b11100000) {
  1095. // 3 byte
  1096. if (i + 4 >= len) continue;
  1097. dest[idx++] = (c & 0b00001111) << 12 |
  1098. (source[++i] & 0b00111111) << 6 |
  1099. (source[++i] & 0b00111111);
  1100. } else if (c & 0b11111000 == 0b11110000) {
  1101. // 4 byte
  1102. if (i + 5 >= len) continue;
  1103. dest[idx++] = (c & 0b00000111) << 18 |
  1104. (source[++i] & 0b00111111) << 12 |
  1105. (source[++i] & 0b00111111) << 6 |
  1106. (source[++i] & 0b00111111);
  1107. } else if (c & 0b11111100 == 0b11111000) {
  1108. // 5 byte
  1109. if (i + 6 >= len) continue;
  1110. dest[idx++] = (c & 0b00000011) << 24 |
  1111. (source[++i] & 0b00111111) << 18 |
  1112. (source[++i] & 0b00111111) << 12 |
  1113. (source[++i] & 0b00111111) << 6 |
  1114. (source[++i] & 0b00111111);
  1115. } else if (c & 0b11111110 == 0b11111100) {
  1116. // 6 byte
  1117. if (i + 7 >= len) continue;
  1118. dest[idx++] = (c & 0b00000001) << 30 |
  1119. (source[++i] & 0b00111111) << 24 |
  1120. (source[++i] & 0b00111111) << 18 |
  1121. (source[++i] & 0b00111111) << 12 |
  1122. (source[++i] & 0b00111111) << 6 |
  1123. (source[++i] & 0b00111111);
  1124. }
  1125. } else {
  1126. dest[idx++] = c;
  1127. }
  1128. }
  1129. dest[idx++] = 0;
  1130. if (heap) {
  1131. RestoreHeapToAddress(heap);
  1132. }
  1133. }
  1134. stock ret_strcatmid(const string[], const source[], start = 0, end = -1) {
  1135. new output[STRLIB_RETURN_SIZE];
  1136. strcat(output, string);
  1137. strcatmid(output, source, start, end);
  1138. return output;
  1139. }
  1140. stock ret_strfrombin(const input[], inputlength = sizeof(input)) {
  1141. new output[STRLIB_RETURN_SIZE];
  1142. strfrombin(output, input, inputlength);
  1143. return output;
  1144. }
  1145. stock ret_strimplode(const glue[], ...) {
  1146. new output[STRLIB_RETURN_SIZE];
  1147. const maxlength = sizeof(output);
  1148. new args = numargs();
  1149. // Loop the variable arguments (the ones after "maxlength").
  1150. for (new arg = 1; arg < args; arg++) {
  1151. // If this isn't the first string, append the glue.
  1152. if (arg != 1)
  1153. strcat(output, glue, maxlength);
  1154. // Wrap these in braces or they will be a part of the above if statement (compiler bug)
  1155. {
  1156. // Get the address of argument no. <arg>
  1157. #emit LCTRL 5
  1158. #emit ADD.C 12
  1159. #emit LOAD.S.alt arg
  1160. #emit SHL.C.alt 2
  1161. #emit ADD
  1162. #emit LOAD.I
  1163. // Push the maxlength, arg address, and output address
  1164. #emit PUSH.C maxlength
  1165. #emit PUSH.pri
  1166. #emit PUSH.ADR output
  1167. // Push the argument count
  1168. #emit PUSH.C 12
  1169. // call strcat
  1170. #emit SYSREQ.C strcat
  1171. // Restore the stack
  1172. #emit STACK 16
  1173. }
  1174. }
  1175. // Fix compiler bug (returning strings in variadic functions)
  1176. #emit LOAD.S.pri 8
  1177. #emit ADD.C 12
  1178. #emit MOVE.alt
  1179. #emit LCTRL 5
  1180. #emit ADD
  1181. #emit LOAD.I
  1182. #emit STOR.S.pri 20
  1183. return output;
  1184. }
  1185. stock ret_strreplace(const string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1) {
  1186. new output[STRLIB_RETURN_SIZE];
  1187. strcat(output, string);
  1188. strreplace(output, search, replacement, ignorecase, pos, limit);
  1189. return output;
  1190. }
  1191. stock ret_strfromliteral(const input[], &pos = 0) {
  1192. new output[STRLIB_RETURN_SIZE];
  1193. strcat(output, input);
  1194. strfromliteral(output, input, pos);
  1195. return output;
  1196. }
  1197. stock ret_strtoliteral(const input[], bool:paranoid = true) {
  1198. new output[STRLIB_RETURN_SIZE];
  1199. strcat(output, input);
  1200. strtoliteral(output, input, paranoid);
  1201. return output;
  1202. }
  1203. stock ret_strtrim(const string[], const chars[] = !"", string_edges:edge = edge_both) {
  1204. new output[STRLIB_RETURN_SIZE];
  1205. strcat(output, string);
  1206. strtrim(output, chars, edge);
  1207. return output;
  1208. }
  1209. stock ret_strpad(const string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"") {
  1210. new output[STRLIB_RETURN_SIZE];
  1211. strcat(output, string);
  1212. strpad(output, length, substr, edge, trim_first, trim_chars);
  1213. return output;
  1214. }
  1215. stock ret_strwrap(const left[], const string[], const right[]) {
  1216. new output[STRLIB_RETURN_SIZE];
  1217. strcat(output, left);
  1218. strcat(output, string);
  1219. strcat(output, right);
  1220. return output;
  1221. }
  1222. stock ret_strurldecode(const input[]) {
  1223. new output[STRLIB_RETURN_SIZE];
  1224. strcat(output, input);
  1225. strurldecode(output, input);
  1226. return output;
  1227. }
  1228. stock ret_strurlencode(const input[], bool:pack = false) {
  1229. new output[STRLIB_RETURN_SIZE];
  1230. strcat(output, input);
  1231. strurlencode(output, input, _, pack);
  1232. return output;
  1233. }
  1234. stock ret_utf8encode(const input[]) {
  1235. new output[STRLIB_RETURN_SIZE];
  1236. utf8encode(output, input);
  1237. return output;
  1238. }
  1239. stock ret_utf8decode(const input[]) {
  1240. new output[STRLIB_RETURN_SIZE];
  1241. utf8decode(output, input);
  1242. return output;
  1243. }
  1244. stock ret_strpack(const source[]) {
  1245. new output[STRLIB_RETURN_SIZE];
  1246. strpack(output, source);
  1247. return output;
  1248. }
  1249. stock ret_strunpack(const source[]) {
  1250. new output[STRLIB_RETURN_SIZE];
  1251. strunpack(output, source);
  1252. return output;
  1253. }
  1254. stock ret_strcat(const string1[], const string2[]) {
  1255. new output[STRLIB_RETURN_SIZE];
  1256. strcat(output, string1);
  1257. strcat(output, string2);
  1258. return output;
  1259. }
  1260. stock ret_strmid(const source[], start, end) {
  1261. new output[STRLIB_RETURN_SIZE];
  1262. strmid(output, source, start, end);
  1263. return output;
  1264. }
  1265. stock ret_strins(const string[], const substr[], pos, maxlength = sizeof(string)) {
  1266. new output[STRLIB_RETURN_SIZE];
  1267. strcat(output, string);
  1268. strins(output, substr, pos);
  1269. return output;
  1270. }
  1271. stock ret_strdel(const string[], start, end) {
  1272. new output[STRLIB_RETURN_SIZE];
  1273. strcat(output, string);
  1274. strdel(output, start, end);
  1275. return output;
  1276. }
  1277. stock ret_valstr(value, bool:pack = false) {
  1278. new output[STRLIB_RETURN_SIZE];
  1279. format(output, sizeof(output), "%d", value);
  1280. if (pack)
  1281. strpack(output, output);
  1282. return output;
  1283. }
  1284. stock ret_GetPlayerName(playerid, bool:pack = false) {
  1285. new output[MAX_PLAYER_NAME];
  1286. GetPlayerName(playerid, output, sizeof(output));
  1287. if (pack)
  1288. strpack(output, output);
  1289. return output;
  1290. }
  1291. stock sprintf(const fmat[], {Float, _}:...) {
  1292. static output[STRLIB_RETURN_SIZE], frm_header[3], heap;
  1293. const output_size = sizeof(output);
  1294. if (ispacked(fmat)) {
  1295. heap = CopyArgumentToHeap(0);
  1296. } else {
  1297. heap = 0;
  1298. }{}
  1299. // Store current frame header
  1300. #emit LCTRL 5
  1301. #emit CONST.alt frm_header
  1302. #emit MOVS 12
  1303. // Change the stack pointer to FRM + 12
  1304. #emit ADD.C 12 // pri is FRM (see above)
  1305. #emit SCTRL 4
  1306. // Push sizeof(output)
  1307. #emit PUSH.C output_size
  1308. // Push output
  1309. #emit PUSH.C output
  1310. // Push the argument count
  1311. #emit LOAD.S.pri 8
  1312. #emit ADD.C 8
  1313. #emit PUSH.pri
  1314. #if !STRLIB_USE_FORMATEX
  1315. const formatex = 0; // Dummy used to avoid "unknown symbol" error
  1316. goto do_sysreq;
  1317. #endif
  1318. // Call formatex (unless this was skipped above)
  1319. #emit LCTRL 6
  1320. #emit ADD.C 36
  1321. #emit PUSH.pri
  1322. #emit CONST.pri formatex
  1323. #emit SCTRL 6
  1324. #if !STRLIB_USE_FORMATEX
  1325. do_sysreq:
  1326. #endif
  1327. // Call format (unless formatex was called, in which case this is skipped)
  1328. #emit SYSREQ.C format
  1329. // Restore the stack pointer to FRM
  1330. #emit LCTRL 5
  1331. #emit SCTRL 4
  1332. // Copy back the frame header
  1333. #emit MOVE.alt
  1334. #emit CONST.pri frm_header
  1335. #emit MOVS 12
  1336. // Restore heap if needed
  1337. if (heap) {
  1338. RestoreHeapToAddress(heap);
  1339. }{}
  1340. // IMPORTANT: Fix compiler bug (returning strings in variadic functions)
  1341. #emit LOAD.S.pri 8
  1342. #emit ADD.C 12
  1343. #emit MOVE.alt
  1344. #emit LCTRL 5
  1345. #emit ADD
  1346. #emit LOAD.I
  1347. #emit STOR.S.pri 20 // 16 + (static_args * 4)
  1348. return output;
  1349. // It is actually used, just not by its symbol name
  1350. #pragma unused fmat
  1351. }