/* Strings manipulation * * by the AMX Mod X Development Team * originally developed by OLO * * This file is provided as is (no warranties). */ #if defined _string_included #endinput #endif #define _string_included #define charsmax(%1) (sizeof(%1)-1) /* Checks if source contains string. On success function * returns position in source, on failure returns -1. */ native contain(const source[],const string[]); /* Checks if source contains string with case ignoring. On success function * returns position in source, on failure returns -1. */ native containi(const source[],const string[]); /* Replaces given string to another in given text. */ native replace(text[], len, const what[], const with[]); /* Adds one string to another. Last parameter different from 0, specifies * how many chars we want to add. Function returns number of all merged chars. */ native add(dest[],len,const src[],max=0); /* Fills string with given format and parameters. * Function returns number of copied chars. * Example: format(dest,"Hello %s. You are %d years old","Tom",17). * If any of your input buffers overlap with the destination buffer, * format() falls back to a "copy-back" version as of 1.65. This is * slower, so you should using a source string that is the same as * the destination. */ native format(output[] ,len ,const format[] , any:...); /* Same as format(), except does not perform a "copy back" check. * This means formatex() is faster, but DOES NOT ALLOW this type * of call: * formatex(buffer, len, "%s", buffer) * formatex(buffer, len, buffer, buffer) * formatex(buffer, len, "%s", buffer[5]) * This is because the output is directly stored into "buffer", * rather than copied back at the end. */ native formatex(output[] ,len ,const format[] , any:...); /* Replacement for format_args. Much faster and %L compatible. * This works exactly like vsnprintf() from C. * You must pass in the output buffer and its size, * the string to format, and the number of the FIRST variable * argument parameter. For example, for: * function (a, b, c, ...) * You would pass 4 (a is 1, b is 2, c is 3, et cetera). * There is no vformatex(). */ native vformat(buffer[], len, const fmt[], vararg); /* * Same as vformat(), except works in normal style dynamic natives. * Instead of passing the format arg string, you can only pass the * actual format argument number itself. * If you pass 0, it will read the format string from an optional * fifth parameter. */ native vdformat(buffer[], len, fmt_arg, vararg, ...); /* Gets parameters from function as formated string. */ native format_args(output[] ,len ,pos = 0); /* Converts number to string. */ native num_to_str(num,string[],len); /* Returns converted string to number. */ native str_to_num(const string[]); /** * Parses the 'string' interpreting its content as an integral number of the specified 'base', * which is returned as integer value. The function also sets the value of 'endPos' to point * to the position of the first character after the number. * * This is the same as C++ strtol function with a difference on second param. * * The function first discards as many whitespace characters as necessary until the first * non-whitespace character is found. Then, starting from this character, takes as many * characters as possible that are valid following a syntax that depends on the 'base' parameter, * and interprets them as a numerical value. Finally, a position of the first character following * the integer representation in 'string' is stored in 'endPos'. * * If the value of 'base' is zero, the syntax expected is similar to that of integer constants, * which is formed by a succession of : * An optional sign character (+ or -) * An optional prefix indicating octal or hexadecimal base ("0" or "0x"/"0X" respectively) * A sequence of decimal digits (if no base prefix was specified) or either octal or hexadecimal digits if a specific prefix is present * * If the 'base' value is between 2 and 36, the format expected for the integral number is a succession * of any of the valid digits and/or letters needed to represent integers of the specified radix * (starting from '0' and up to 'z'/'Z' for radix 36). The sequence may optionally be preceded by * a sign (either + or -) and, if base is 16, an optional "0x" or "0X" prefix. * * If the first sequence of non-whitespace characters in 'string' is not a valid integral number * as defined above, or if no such sequence exists because either 'string' is empty or it contains * only whitespace characters, no conversion is performed. * * @param string The string to parse. * @param endPos The position of the first character following the number. * On success and when containing only numbers, position is at the end of string, meaning equal to 'string' length. * On failure, position is sets always to 0. * @param base The numerical base (radix) that determines the valid characters and their interpretation. * If this is 0, the base used is determined by the format in the sequence. * @return On success, the function returns the converted integral number as integer value. * If no valid conversion could be performed, a zero value is returned. * If the value read is out of the range of representable values by a cell, * the function returns 'cellmin' or 'cellmax'. */ native strtol(const string[], &endPos = 0, base = 0); /** * Parses the 'string' interpreting its content as an floating point number and returns its value as a float. * The function also sets the value of 'endPos' to point to the position of the first character after the number. * * This is the same as C++ strtod function with a difference on second param. * * The function first discards as many whitespace characters as necessary until the first * non-whitespace character is found. Then, starting from this character, takes as many * characters as possible that are valid and interprets them as a numerical value. * Finally, a position of the first character following the float representation in 'string' * is stored in 'endPos'. * * If the first sequence of non-whitespace characters in 'string' is not a valid float number * as defined above, or if no such sequence exists because either 'string' is empty or it contains * only whitespace characters, no conversion is performed. * * @param string The string to parse. * @param endPos The position of the first character following the number. * On success and when containing only numbers, position is at the end of string, meaning equal to 'string' length. * On failure, position is sets always to 0. * @return On success, the function returns the converted floating point number as float value. * If no valid conversion could be performed, a zero value is returned. */ native Float:strtof(const string[], &endPos = 0); /* Converts float to string. */ native float_to_str(Float:fl, string[], len); /* Parses a float. */ native Float:str_to_float(const string[]); /* Checks if two strings equal. If len var is set * then there are only c chars comapred. */ native equal(const a[],const b[],c=0); /* Checks if two strings equal with case ignoring. * If len var is set then there are only c chars comapred. */ native equali(const a[],const b[],c=0); /* Copies one string to another. By len var * you may specify max. number of chars to copy. */ native copy(dest[],len,const src[]); /* Copies one string to another until char ch is found. * By len var you may specify max. number of chars to copy. */ native copyc(dest[],len,const src[],ch); /* Sets string with given character. */ native setc(src[],len,ch); /* Gets parameters from text. * Example: to split text: "^"This is^" the best year", * call function like this: parse(text,arg1,len1,arg2,len2,arg3,len3,arg4,len4) * and you will get: "This is", "the", "best", "year" * Function returns number of parsed parameters. */ native parse(const text[], ... ); /* Breaks a string into two halves, by token. See strbreak() for doing this with parameters. Example: str1[] = This *is*some text strtok(str1, left, 24, right, 24, '*') left will be "This " Right will be "is*some text" If you use trimSpaces, all spaces are trimmed from Left. */ native strtok(const text[], Left[], leftLen, Right[], rightLen, token=' ', trimSpaces=0); /** * Below are the trim flags for strtok2 * * You can specify how the left and right buffers will * be trimmed by strtok2. LTRIM trims spaces from the * left side. RTRIM trims from the right side. * * The defines TRIM_INNER, TRIM_OUTER and TRIM_FULL are * shorthands for commonly used flag combinations. * * When the initial string is trimmed, using TRIM_INNER * for all subsequent strtok2 calls will ensure that left * and right are always trimmed from both sides. * * Examples: * str1[] = " This is * some text " * strtok2(str1, left, 24, right, 24, '*', TRIM_FULL) * left will be "This is", right will be "some text" * * str2[] = " Here is | an | example " * trim(str2) * strtok2(str2, left, 24, right, 24, '|', TRIM_INNER) * left will be "Here is", right will be "an | example" * strtok2(right, left, 24, right, 24, '|', TRIM_INNER) * left will be "an", right will be "example" * * str3[] = " One - more " * strtok2(str3, left, 24, right, 24, '-', TRIM_OUTER) * left will be "One ", right will be " more" * * str4[] = " Final . example " * strtok2(str4, left, 24, right, 24, '.', LTRIM_LEFT|LTRIM_RIGHT) * left will be "Final ", right will be "example " */ #define LTRIM_LEFT (1<<0) #define RTRIM_LEFT (1<<1) #define LTRIM_RIGHT (1<<2) #define RTRIM_RIGHT (1<<3) #define TRIM_INNER RTRIM_LEFT|LTRIM_RIGHT #define TRIM_OUTER LTRIM_LEFT|RTRIM_RIGHT #define TRIM_FULL TRIM_OUTER|TRIM_INNER /** * Breaks a string in two by token * * Only available in 1.8.3 and above * * @param text String to tokenize * @param left Buffer to store left half * @param llen Size of left buffer * @param right Buffer to store right half * @param rlen Size of right buffer * @param token Token to split by * @param trim Flags for trimming behavior, see above * * @return Returns position of token in string if found, * -1 if token was not found */ native strtok2(const text[], left[], const llen, right[], const rlen, const token = ' ', const trim = 0); /* Strips spaces from the beginning and end of a string. */ native trim(text[]); /* Converts all chars in string to lower case. */ native strtolower(string[]); /* Converts all chars in string to upper case. */ native strtoupper(string[]); /* Make a string's first character uppercase */ native ucfirst(string[]); /* Returns true when value is digit. */ native isdigit(ch); /* Returns true when value is letter. */ native isalpha(ch); /* Returns true when value is space. */ native isspace(ch); /* Returns true when value is letter or digit. */ native isalnum(ch); /* Concatenates a string. Maxlength is the total buffer of the destination. */ native strcat(dest[], const source[], maxlength); /* Finds a string in another string. Returns -1 if not found. */ native strfind(const string[], const sub[], ignorecase=0, pos=0); /* Compares two strings with the C function strcmp(). Returns 0 on equal. */ native strcmp(const string1[], const string2[], ignorecase=0); /* Tests if given string contains only digits. Also, returns false for zero-length strings. */ stock bool:is_str_num(const sString[]) { new i = 0; while (sString[i] && isdigit(sString[i])) ++i; return sString[i] == 0 && i != 0; } // Warning: this function is deprecated as it does not work properly. Use // argparse() or argbreak(). native strbreak(const text[], Left[], leftLen, Right[], rightLen); /** * Parses an argument string to find the first argument. You can use this to * replace strbreak(). * * You can use argparse() to break a string into all of its arguments: * new arg[N], pos; * while (true) { * pos = argparse(string, pos, arg, sizeof(arg) - 1); * if (pos == -1) * break; * } * * All initial whitespace is removed. Remaining characters are read until an * argument separator is encountered. A separator is any whitespace not inside * a double-quotation pair (i.e. "x b" is one argument). If only one quotation * mark appears, argparse() acts as if one existed at the end of the string. * Quotation marks are never written back, and do not act as separators. For * example, "a""b""c" will return "abc". An empty quote pair ("") will count * as an argument containing no characters. * * argparse() will write an empty string to argbuffer if no argument is found. * * @param text String to tokenize. * @param pos Position to start parsing from. * @param argbuffer Buffer to store first argument. * @param maxlen Size of the buffer. * @return If no argument was found, -1 is returned. Otherwise, * the index to the next position to parse from is * returned. This might be the very end of the string. */ native argparse(const text[], pos, argbuffer[], maxlen); /* Emulates strbreak() using argparse(). */ stock argbreak(const text[], left[], leftlen, right[], rightlen) { new pos = argparse(text, 0, left, leftlen); if (pos == -1) return -1; new textlen = strlen(text); while (pos < textlen && isspace(text[pos])) pos++; copy(right, rightlen, text[pos]); return pos; } /* It is basically strbreak but you have a delimiter that is more than one character in length. You pass the Input string, the Left output, the max length of the left output, the right output , the max right length, and then the delimiter string. By Suicid3 */ stock split(const szInput[], szLeft[], pL_Max, szRight[], pR_Max, const szDelim[]) { new iEnd = contain(szInput, szDelim); new iStart = iEnd + strlen(szDelim); //If delimiter isnt in Input just split the string at max lengths if (iEnd == -1) { iStart = copy(szLeft, pL_Max, szInput); copy(szRight, pR_Max, szInput[iStart]); return; } //If delimter is in Input then split at input for max lengths if (pL_Max >= iEnd) copy(szLeft, iEnd, szInput); else copy(szLeft, pL_Max, szInput); copy(szRight, pR_Max, szInput[iStart]); return; } /* Removes a path from szFilePath leaving the name of the file in szFile for a pMax length. */ stock remove_filepath(const szFilePath[], szFile[], pMax) { new len = strlen(szFilePath); while ((--len >= 0) && (szFilePath[len] != '/') && (szFilePath[len] != '\')) { } copy(szFile, pMax, szFilePath[len + 1]); return; } /* Replaces a contained string iteratively. * This ensures that no infinite replacements will take place by * intelligently moving to the next string position each iteration. */ stock replace_all(string[], len, const what[], const with[]) { new pos = 0; if ((pos = contain(string, what)) == -1) { return 0; } new total = 0; new with_len = strlen(with); new diff = strlen(what) - with_len; new total_len = strlen(string); new temp_pos = 0; while (replace(string[pos], len - pos, what, with) != 0) { total++; /* jump to position after replacement */ pos += with_len; /* update cached length of string */ total_len -= diff; /* will the next call be operating on the last character? */ if (pos >= total_len) { break; } /* find the next position from our offset */ temp_pos = contain(string[pos], what); /* if it's invalid, we're done */ if (temp_pos == -1) { break; } /* otherwise, reposition and update counters */ pos += temp_pos; } return total; }