| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- /*
- * Version: MPL 1.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the sscanf 2.0 SA:MP plugin.
- *
- * The Initial Developer of the Original Code is Alex "Y_Less" Cole.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Special Thanks to:
- *
- * SA:MP Team past, present and future
- */
- #include "SDK/amx/amx.h"
- #include <string.h>
- #include "utils.h"
- #include "specifiers.h"
- #include "data.h"
- #include "sscanf.h"
- extern logprintf_t
- logprintf;
- #define SAVE_VALUE(m) \
- if (doSave) \
- *cptr++ = m
- #define SAVE_VALUE_F(m) \
- if (doSave) { \
- float f = (float)m; \
- *cptr++ = amx_ftoc(f); }
- // Macros for the regular values.
- #define DO(m,n) \
- {m b; \
- if (Do##n(&string, &b)) { \
- SAVE_VALUE((cell)b); \
- break; } \
- *input = string; \
- return SSCANF_FAIL_RETURN; }
- #define DOV(m,n) \
- {m b; \
- Do##n(&string, &b); \
- SAVE_VALUE((cell)b); }
- #define DOF(m,n) \
- {m b; \
- if (Do##n(&string, &b)) { \
- SAVE_VALUE_F(b) \
- break; } \
- *input = string; \
- return SSCANF_FAIL_RETURN; }
- #define OPTIONAL_INVALID \
- logprintf("sscanf warning: Optional types invalid in enum specifiers, consider using 'E'.")
- #define DX(m,n) \
- OPTIONAL_INVALID;
- #define DXF(m,n) \
- OPTIONAL_INVALID;
- extern AMX *
- g_aCurAMX;
- extern int
- gOptions;
- bool
- DoK(AMX * amx, char ** defaults, char ** input, cell * cptr, bool optional);
- int
- DoEnumValues(char * format, char ** input, cell * cptr, bool defaults)
- {
- // If cptr is NULL we never save - regardless of quiet sections.
- bool
- doSave = cptr != NULL;
- char *
- string = *input;
- // Copied directly from the main loop, just with different macros.
- while (*string)
- {
- if (!*format)
- {
- // Happy with this return.
- *input = string;
- return SSCANF_TRUE_RETURN;
- }
- else if (IsWhitespace(*format))
- {
- ++format;
- }
- else
- {
- switch (*format++)
- {
- case 'L':
- DX(bool, L)
- // FALLTHROUGH
- case 'l':
- DOV(bool, L)
- break;
- case 'B':
- DX(int, B)
- // FALLTHROUGH
- case 'b':
- DO(int, B)
- case 'N':
- DX(int, N)
- // FALLTHROUGH
- case 'n':
- DO(int, N)
- case 'C':
- DX(char, C)
- // FALLTHROUGH
- case 'c':
- DO(char, C)
- case 'I':
- case 'D':
- DX(int, I)
- // FALLTHROUGH
- case 'i':
- case 'd':
- DO(int, I)
- case 'H':
- case 'X':
- DX(int, H)
- // FALLTHROUGH
- case 'h':
- case 'x':
- DO(int, H)
- case 'O':
- DX(int, O)
- // FALLTHROUGH
- case 'o':
- DO(int, O)
- case 'F':
- DXF(double, F)
- // FALLTHROUGH
- case 'f':
- DOF(double, F)
- case 'G':
- DXF(double, G)
- // FALLTHROUGH
- case 'g':
- DOF(double, G)
- case '{':
- if (doSave)
- {
- doSave = false;
- }
- else if (cptr)
- {
- // Already in a quiet section.
- logprintf("sscanf warning: Can't have nestled quiet sections.");
- }
- continue;
- case '}':
- if (doSave)
- {
- logprintf("sscanf warning: Not in a quiet section.");
- }
- else
- {
- if (cptr)
- {
- doSave = true;
- }
- else
- {
- logprintf("sscanf warning: Can't remove quiet in enum.");
- }
- }
- continue;
- case 'P':
- {
- ResetDelimiter();
- char *
- t = GetMultiType(&format);
- if (t) AddDelimiters(t);
- else return SSCANF_FAIL_RETURN;
- continue;
- }
- //logprintf("sscanf warning: You can't have an optional delimiter.");
- // FALLTHROUGH
- case 'p':
- // 'P' doesn't exist.
- // Theoretically, for compatibility, this should be:
- // p<delimiter>, but that will break backwards
- // compatibility with anyone doing "p<" to use '<' as a
- // delimiter (doesn't matter how rare that may be). Also,
- // writing deprecation code and both the new and old code
- // is more trouble than it's worth, and it's slow.
- // UPDATE: I wrote the "GetSingleType" code for 'a' and
- // figured out a way to support legacy and new code, while
- // still maintaining support for the legacy "p<" separator,
- // so here it is:
- ResetDelimiter();
- AddDelimiter(GetSingleType(&format));
- continue;
- case 'K':
- OPTIONAL_INVALID;
- // FALLTHROUGH
- case 'k':
- //DOF(double, K)
- if (defaults && !(gOptions & 16))
- {
- GetMultiType(&format);
- if (doSave)
- {
- int
- b;
- DoI(&string, &b);
- *cptr++ = b;
- }
- else
- {
- int
- b;
- DoI(&string, &b);
- }
- *(format - 1) = '>';
- break;
- }
- else if (doSave)
- {
- if (DoK(g_aCurAMX, &format, &string, cptr, false))
- {
- *(format - 1) = '>';
- ++cptr;
- break;
- }
- }
- else
- {
- if (DoK(g_aCurAMX, &format, &string, NULL, false))
- {
- *(format - 1) = '>';
- break;
- }
- }
- *input = string;
- return SSCANF_FAIL_RETURN;
- case 'Z':
- logprintf("sscanf warning: 'Z' doesn't exist - that would be an optional, deprecated optional string!.");
- // FALLTHROUGH
- case 'z':
- logprintf("sscanf warning: 'z' is deprecated, consider using 'S' instead.");
- // FALLTHROUGH
- case 'S':
- OPTIONAL_INVALID;
- // FALLTHROUGH
- case 's':
- {
- // Get the length.
- int
- lole = GetLength(&format, true);
- if (!lole)
- {
- return SSCANF_FAIL_RETURN;
- }
- char *
- dest;
- DoS(&string, &dest, lole, IsEnd(*format));
- // Send the string to PAWN.
- if (doSave)
- {
- // Save the string.
- amx_SetString(cptr, dest, 0, 0, lole);
- // Increase the pointer by the MAXIMUM length of
- // the string - that's how enum strings work.
- cptr += lole;
- }
- }
- break;
- case 'U':
- DX(int, U)
- // FALLTHROUGH
- case 'u':
- if (*format == '[')
- {
- logprintf("sscanf warning: User arrays are not supported in enums.");
- SkipLength(&format);
- }
- #define DoU(m,n) DoU(m,n,0)
- if (defaults && !(gOptions & 1))
- {
- DOV(int, I)
- }
- else
- {
- DOV(int, U)
- }
- #undef DoU
- break;
- case 'Q':
- DX(int, Q)
- // FALLTHROUGH
- case 'q':
- if (*format == '[')
- {
- logprintf("sscanf warning: User arrays are not supported in enums.");
- SkipLength(&format);
- }
- #define DoQ(m,n) DoQ(m,n,0)
- if (defaults && !(gOptions & 1))
- {
- DOV(int, I)
- }
- else
- {
- DOV(int, Q)
- }
- #undef DoQ
- break;
- case 'R':
- DX(int, R)
- // FALLTHROUGH
- case 'r':
- if (*format == '[')
- {
- logprintf("sscanf warning: User arrays are not supported in enums.");
- SkipLength(&format);
- }
- #define DoR(m,n) DoR(m,n,0)
- if (defaults && !(gOptions & 1))
- {
- DOV(int, I)
- }
- else
- {
- DOV(int, R)
- }
- #undef DoR
- break;
- case 'A':
- case 'a':
- logprintf("sscanf error: Arrays are not supported in enums.");
- *input = string;
- return SSCANF_FAIL_RETURN;
- case 'E':
- case 'e':
- logprintf("sscanf error: Enums are not supported in enums.");
- *input = string;
- return SSCANF_FAIL_RETURN;
- case '\'':
- // Find the end of the literal.
- {
- char
- * str = format,
- * write = format;
- bool
- escape = false;
- while (!IsEnd(*str) && (escape || *str != '\''))
- {
- if (*str == '\\')
- {
- if (escape)
- {
- // "\\" - Go back a step to write this
- // character over the last character (which
- // just happens to be the same character).
- --write;
- }
- escape = !escape;
- }
- else
- {
- if (*str == '\'')
- {
- // Overwrite the escape character with the
- // quote character. Must have been
- // preceeded by a slash or it wouldn't have
- // got to here in the loop.
- --write;
- }
- escape = false;
- }
- // Copy the string over itself to get rid of excess
- // escape characters.
- // Not sure if it's faster in the average case to
- // always do the copy or check if it's needed.
- // This write is always safe as it makes the string
- // shorter, so we'll never run out of space. It
- // will also not overwrite the original string.
- *write++ = *str++;
- }
- if (*str == '\'')
- {
- // Correct end. Make a shorter string to search
- // for.
- *write = '\0';
- // Find the current section of format in string.
- char *
- find = strstr(string, format);
- if (!find)
- {
- // Didn't find the string.
- *input = string;
- return SSCANF_FAIL_RETURN;
- }
- // Found the string. Update the current string
- // position to the length of the search term
- // further along from the start of the term. Use
- // "write" here as we want the escaped string
- // length.
- string = find + (write - format);
- // Move to after the end of the search string. Use
- // "str" here as we want the unescaped string
- // length.
- format = str + 1;
- }
- else
- {
- logprintf("sscanf warning: Unclosed string literal.");
- char *
- find = strstr(string, format);
- if (!find)
- {
- *input = string;
- return SSCANF_FAIL_RETURN;
- }
- string = find + (write - format);
- format = str;
- }
- }
- break;
- case '?':
- logprintf("sscanf error: Options are not supported in enums.");
- return SSCANF_FAIL_RETURN;
- case '%':
- logprintf("sscanf warning: sscanf specifiers do not require '%' before them.");
- continue;
- case '-':
- {
- int
- len = 1;
- switch (*format++)
- {
- case 'i':
- case 'f':
- case 'l':
- case 'b':
- case 'n':
- case 'c':
- case 'd':
- case 'h':
- case 'x':
- case 'o':
- case 'g':
- break;
- case 'I':
- case 'F':
- case 'L':
- case 'B':
- case 'N':
- case 'C':
- case 'D':
- case 'H':
- case 'X':
- case 'O':
- case 'G':
- OPTIONAL_INVALID;
- break;
- case 'K':
- OPTIONAL_INVALID;
- case 'k':
- GetMultiType(&format);
- break;
- case 'P':
- case 'p':
- logprintf("sscanf warning: A minus delimiter makes no sense.");
- len = 0;
- break;
- case '{':
- case '}':
- logprintf("sscanf warning: A minus quiet section makes no sense.");
- len = 0;
- break;
- case 'U':
- case 'Q':
- case 'R':
- OPTIONAL_INVALID;
- case 'u':
- case 'q':
- case 'r':
- if (*format == '[')
- {
- len = GetLength(&format, true);
- }
- break;
- case 'A':
- OPTIONAL_INVALID;
- case 'a':
- len = GetLength(&format, true);
- break;
- case 'E':
- OPTIONAL_INVALID;
- case 'e':
- logprintf("sscanf error: Enums are not supported in enums.");
- *input = string;
- return SSCANF_FAIL_RETURN;
- case 'Z':
- logprintf("sscanf warning: 'Z' doesn't exist - that would be an optional, deprecated optional string!.");
- // FALLTHROUGH
- case 'z':
- logprintf("sscanf warning: 'z' is deprecated, consider using 'S' instead.");
- // FALLTHROUGH
- case 'S':
- OPTIONAL_INVALID;
- // FALLTHROUGH
- case 's':
- len = GetLength(&format, true);
- break;
- case '?':
- logprintf("sscanf warning: A minus option makes no sense.");
- len = 0;
- break;
- case '%':
- logprintf("sscanf warning: sscanf specifiers do not require '%' before them.");
- len = 0;
- break;
- case '-':
- logprintf("sscanf warning: A minus minus makes no sense.");
- len = 0;
- break;
- default:
- logprintf("sscanf warning: Unknown format specifier '%c', skipping.", *(format - 1));
- len = 0;
- break;
- }
- if (doSave)
- {
- cptr += len;
- }
- }
- break;
- default:
- logprintf("sscanf warning: Unknown format specifier '%c', skipping.", *(format - 1));
- continue;
- }
- // Loop cleanup - only skip one spacer so that we can detect
- // multiple explicit delimiters in a row, for example:
- //
- // hi there
- //
- // is NOT multiple explicit delimiters in a row (they're
- // whitespace). This however is:
- //
- // hi , , , there
- //
- SkipOneSpacer(&string);
- }
- }
- // Save the end of the string.
- *input = string;
- if (*format)
- {
- if (*format == '}' && IsEnd(*(format + 1)))
- {
- return SSCANF_TRUE_RETURN;
- }
- // Ran out of values - check if this is an optional array at a higher
- // level.
- return SSCANF_CONT_RETURN;
- }
- return SSCANF_TRUE_RETURN;
- }
- bool
- DoE(char ** defaults, char ** input, cell * cptr, bool optional)
- {
- // First, get the type of the array.
- char *
- type = GetMultiType(defaults);
- if (!type)
- {
- return false;
- }
- if (optional)
- {
- // Optional parameters - just collect the data for
- // now and use it later.
- if (**defaults == '(')
- {
- ++(*defaults);
- SkipWhitespace(defaults);
- // Got the start of the values.
- char *
- opts = *defaults;
- // Skip the defaults for now, we don't know the length yet.
- bool
- escape = false;
- while (**defaults && (escape || **defaults != ')'))
- {
- if (**defaults == '\\')
- {
- escape = !escape;
- }
- else
- {
- escape = false;
- }
- ++(*defaults);
- }
- if (**defaults)
- {
- if (opts == *defaults)
- {
- // No defaults found.
- logprintf("sscanf warning: Empty default values.");
- optional = false;
- }
- // Found a valid end. Make it null for
- // later array getting (easy to detect a
- // null end and we'll never need to
- // backtrack to here in the specifiers).
- **defaults = '\0';
- ++(*defaults);
- }
- else
- {
- logprintf("sscanf warning: Unclosed default value.");
- }
- if (optional)
- {
- // Optional parameters are always separated by commans, not
- // whatever the coder may choose.
- TempDelimiter(",)");
- // We need to copy the old save value for optional parts. If
- // we don't and save gets set to false in the middle of the
- // enum then when the code is called for a second time for the
- // real values then save will already be false and they won't
- // get saved.
- switch (DoEnumValues(type, &opts, cptr, true))
- {
- case SSCANF_TRUE_RETURN:
- break;
- case SSCANF_CONT_RETURN:
- logprintf("sscanf error: Insufficient default values.");
- // FALLTHROUGH
- default:
- RestoreDelimiter();
- return false;
- }
- RestoreDelimiter();
- }
- }
- else
- {
- logprintf("sscanf warning: No default value found.");
- optional = false;
- }
- }
- if (input)
- {
- switch (DoEnumValues(type, input, cptr, false))
- {
- case SSCANF_TRUE_RETURN:
- return true;
- case SSCANF_CONT_RETURN:
- if (optional)
- {
- return true;
- }
- // FALLTHROUGH
- default:
- return false;
- }
- }
- return true;
- }
|