| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929 |
- /*
- * 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 <string.h>
- #include <stdlib.h>
- #include "sscanf.h"
- #include "utils.h"
- #include "data.h"
- #include "specifiers.h"
- extern logprintf_t
- logprintf;
- extern int
- gOptions;
- extern unsigned int
- g_iTrueMax,
- g_iInvalid,
- g_iMaxPlayerName;
- //extern int
- // g_iServerVersion;
- bool
- DoI(char ** input, int * ret)
- {
- *ret = GetDec(input);
- return GetReturn(input);
- }
- bool
- DoN(char ** input, int * ret)
- {
- *ret = GetNumber(input);
- return GetReturn(input);
- }
- bool
- DoH(char ** input, int * ret)
- {
- *ret = GetHex(input);
- return GetReturn(input);
- }
- bool
- DoO(char ** input, int * ret)
- {
- *ret = GetOct(input);
- return GetReturn(input);
- }
- bool
- DoF(char ** input, double * ret)
- {
- *ret = strtod(*input, input);
- return GetReturn(input);
- }
- bool
- DoC(char ** input, char * ret)
- {
- *ret = **input;
- ++(*input);
- if (*ret == '\\')
- {
- if (IsSpacer(**input))
- {
- // '\ '
- *ret = **input;
- ++(*input);
- }
- else if (**input == '\\')
- {
- // '\\'
- ++(*input);
- }
- }
- return GetReturn(input);
- }
- bool
- DoL(char ** input, bool * ret)
- {
- *ret = GetLogical(input);
- return true;
- }
- bool
- DoB(char ** input, int * ret)
- {
- *ret = (int)GetBool(input);
- return GetReturn(input);
- }
- bool
- DoG(char ** input, double * ret)
- {
- char *
- string = *input;
- int
- temp = 0;
- switch (*string)
- {
- case 'N':
- case 'n':
- if (strichecks(string, "NAN_E"))
- {
- *input += 5;
- temp = FLOAT_NAN_E;
- }
- else if (strichecks(string, "NAN"))
- {
- *input += 3;
- temp = FLOAT_NAN;
- }
- else if (strichecks(string, "NEG_INFINITY"))
- {
- *input += 12;
- temp = FLOAT_NEG_INFINITY;
- }
- else if (strichecks(string, "NEGATIVE_INFINITY"))
- {
- *input += 17;
- temp = FLOAT_NEG_INFINITY;
- }
- *ret = (double)(*((float *)&temp));
- break;
- case 'I':
- case 'i':
- if (strichecks(string, "INFINITY"))
- {
- *input += 8;
- temp = FLOAT_INFINITY;
- *ret = (double)(*((float *)&temp));
- }
- break;
- case '-':
- if (strichecks(string + 1, "INFINITY"))
- {
- *input += 9;
- temp = FLOAT_NEG_INFINITY;
- *ret = (double)(*((float *)&temp));
- break;
- }
- // FALLTHROUGH
- default:
- // Read in the value and save the pointer - may as well use
- // existing, pre set up variables.
- *ret = strtod(string, input);
- break;
- }
- return GetReturn(input);
- }
- bool
- DoS(char ** input, char ** ret, int length, bool all)
- {
- // Don't reallocate the memory, just set pointers to the current data and
- // add nulls to terminate.
- int
- i = 0;
- // Save the pointer to the start of the data.
- *ret = *input;
- // Check if we want the whole remaining string or just the next word.
- char
- * string = *input,
- * outp = string;
- if (all)
- {
- // Everything.
- while (!IsEnd(*string))
- {
- ++i;
- // Cap at "length" characters long.
- if (i == length)
- {
- // Could let the loop exit properly as it would next time, but
- // there's no point - it's just extra work and we know it's OK.
- // We set the null before incrementing to ensure it's included
- // in the output.
- logprintf("sscanf warning: String buffer overflow.");
- // Removed the break - discard the rest of the string.
- //break;
- }
- else if (i < length)
- {
- if (*string == '\\')
- {
- if (IsEnd(*(string + 1)))
- {
- ++string;
- break;
- }
- // You can only escape the escape character or spaces.
- // Spaces aren't technically used here, but they COULD be
- // used in the current string in other circumstances, so
- // they may be present even when not needed.
- if (*(string + 1) == '\\' || IsSpacer(*(string + 1)))
- {
- ++string;
- }
- }
- if (outp != string)
- {
- // Compress the string only if we have a discrepancy
- // between input and output pos.
- *outp = *string;
- }
- ++outp;
- }
- ++string;
- }
- }
- else if (IsDefaultDelimiter())
- {
- while (!IsWhitespace(*string))
- {
- ++i;
- if (i == length)
- {
- logprintf("sscanf warning: String buffer overflow.");
- }
- else if (i < length)
- {
- if (*string == '\\')
- {
- if (IsEnd(*(string + 1)))
- {
- ++string;
- break;
- }
- if (*(string + 1) == '\\' || IsWhitespace(*(string + 1)))
- {
- ++string;
- }
- }
- if (outp != string)
- {
- *outp = *string;
- }
- ++outp;
- }
- ++string;
- }
- }
- else
- {
- // Just a single word. Note that if your delimiter is a backslash you
- // can't escape it - this is not a bug, just don't try use it as a
- // delimiter and still expect to be able to use it in a string.
- while (!IsEnd(*string) && !IsDelimiter(*string))
- {
- ++i;
- if (i == length)
- {
- logprintf("sscanf warning: String buffer overflow.");
- }
- else if (i < length)
- {
- if (*string == '\\')
- {
- if (IsEnd(*(string + 1)))
- {
- ++string;
- break;
- }
- // Escape spaces, backspace and delimiters - this code is
- // context independent so you can use a string with or
- // without a delimiter and can still escape spaces.
- if (*(string + 1) == '\\' || IsSpacer(*(string + 1)))
- {
- ++string;
- }
- }
- if (outp != string)
- {
- *outp = *string;
- }
- ++outp;
- }
- ++string;
- }
- }
- if (!IsEnd(*string))
- {
- // Skip the final character.
- *input = string + 1;
- }
- else
- {
- // Save the return.
- *input = string;
- }
- // Add a null terminator.
- *outp = '\0';
- // Can't really fail on a string as everything goes!
- return true;
- }
- bool
- DoU(char ** input, int * ret, unsigned int start)
- {
- char
- * end = NULL,
- * string = *input;
- unsigned int
- val = GetUserString(string, &end);
- if (val < g_iTrueMax && IsPlayerConnected(val))
- {
- *input = end;
- *ret = val;
- return false;
- }
- else
- {
- *ret = g_iInvalid;
- int
- * conn = GetConnected(),
- len = end - string;
- val = start;
- conn += start;
- char
- tmp = *end,
- * name = GetNames();
- *end = '\0';
- switch (gOptions & 6)
- {
- case 0:
- // Original.
- while (val < g_iTrueMax)
- {
- if (*conn++ && !strincmp(name, string, len))
- {
- break;
- }
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 2:
- // Partial matches.
- while (val < g_iTrueMax)
- {
- if (*conn++ && strstrin(name, string, len))
- {
- break;
- }
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 4:
- // Multiple matches.
- while (val < g_iTrueMax)
- {
- if (*conn++ && !strincmp(name, string, len))
- {
- if (*ret != g_iInvalid)
- {
- val = 0x80000000;
- break;
- }
- *ret = val;
- }
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 6:
- // Both.
- while (val < g_iTrueMax)
- {
- if (*conn++ && strstrin(name, string, len))
- {
- if (*ret != g_iInvalid)
- {
- val = 0x80000000;
- break;
- }
- *ret = val;
- }
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- }
- *end = tmp;
- }
- *input = end;
- if (val != g_iTrueMax)
- {
- *ret = val;
- }
- return true;
- }
- bool
- DoQ(char ** input, int * ret, unsigned int start)
- {
- char
- * end = NULL,
- * string = *input;
- // Get the string.
- unsigned int
- val = GetUserString(string, &end);
- // We only have a less than check here as valid IDs start at 0, and
- // GetUserString can't return numbers less than 0, it's physically
- // impossible for it to do so as there's no code there for it to happen.
- // Check that the player is a valid ID, connected and a non-player
- // character, and if one of those checks FAILS, do the code below. We
- // could attempt to write a C++ equivalent to foreach here, but this should
- // be pretty fast anyway as it uses direct memory access.
- if (val < g_iTrueMax && IsPlayerConnected(val) && IsPlayerNPC(val))
- {
- *input = end;
- *ret = val;
- return false;
- }
- else
- {
- *ret = g_iInvalid;
- // Find the NPC by name.
- int
- * conn = GetConnected(),
- * npc = GetNPCs(),
- len = end - string;
- val = start;
- conn += start;
- npc += start;
- // Save the end character for the name.
- char
- tmp = *end,
- * name = GetNames();
- // Make the input string shorter for comparison.
- *end = '\0';
- switch (gOptions & 6)
- {
- case 0:
- // Original.
- while (val < g_iTrueMax)
- {
- if (*conn && *npc && !strincmp(name, string, len))
- {
- break;
- }
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 2:
- // Partial matches.
- while (val < g_iTrueMax)
- {
- if (*conn && *npc && strstrin(name, string, len))
- {
- break;
- }
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 4:
- // Multiple matches.
- while (val < g_iTrueMax)
- {
- if (*conn && *npc && !strincmp(name, string, len))
- {
- if (*ret != g_iInvalid)
- {
- val = 0x80000000;
- break;
- }
- *ret = val;
- }
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 6:
- // Both.
- // Loop through all the players and check that they're
- // connected, an NPC, and that their name is correct.
- while (val < g_iTrueMax)
- {
- if (*conn && *npc && strstrin(name, string, len))
- {
- if (*ret != g_iInvalid)
- {
- val = 0x80000000;
- break;
- }
- *ret = val;
- }
- // Can't do *npc++ above as it's not always reached and we
- // need it to be incremented (short circuiting).
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- }
- *end = tmp;
- }
- // Save the pointer to the end of the name.
- *input = end;
- // Optimised from the PAWN version. If it gets to here without having
- // found a valid player then 'val' will be g_iTrueMax.
- if (val != g_iTrueMax)
- {
- *ret = val;
- }
- return true;
- }
- bool
- DoR(char ** input, int * ret, unsigned int start)
- {
- char
- * end = NULL,
- * string = *input;
- unsigned int
- val = GetUserString(string, &end);
- if (val < g_iTrueMax && IsPlayerConnected(val) && !IsPlayerNPC(val))
- {
- *input = end;
- *ret = val;
- return false;
- }
- else
- {
- *ret = g_iInvalid;
- int
- * conn = GetConnected(),
- * npc = GetNPCs(),
- len = end - string;
- val = start;
- conn += start;
- npc += start;
- char
- tmp = *end,
- * name = GetNames();
- *end = '\0';
- switch (gOptions & 6)
- {
- case 0:
- // Original.
- while (val < g_iTrueMax)
- {
- if (*conn && !*npc && !strincmp(name, string, len))
- {
- break;
- }
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 2:
- // Partial matches.
- while (val < g_iTrueMax)
- {
- if (*conn && !*npc && strstrin(name, string, len))
- {
- break;
- }
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 4:
- // Multiple matches.
- while (val < g_iTrueMax)
- {
- if (*conn && !*npc && !strincmp(name, string, len))
- {
- if (*ret != g_iInvalid)
- {
- val = 0x80000000;
- break;
- }
- *ret = val;
- }
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- case 6:
- // Both.
- // Loop through all the players and check that they're
- // connected, an NPC, and that their name is correct.
- while (val < g_iTrueMax)
- {
- if (*conn && !*npc && strstrin(name, string, len))
- {
- if (*ret != g_iInvalid)
- {
- val = 0x80000000;
- break;
- }
- *ret = val;
- }
- // Can't do *npc++ above as it's not always reached and we
- // need it to be incremented (short circuiting).
- ++conn;
- ++npc;
- name += g_iMaxPlayerName;
- ++val;
- }
- break;
- }
- *end = tmp;
- }
- *input = end;
- if (val != g_iTrueMax)
- {
- *ret = val;
- }
- return true;
- }
- bool
- DoID(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- *ret = GetDec(input);
- return GetReturnDefault(input);
- }
- bool
- DoND(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- *ret = GetNumber(input);
- return GetReturnDefault(input);
- }
- bool
- DoHD(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- *ret = GetHex(input);
- return GetReturnDefault(input);
- }
- bool
- DoOD(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- *ret = GetOct(input);
- return GetReturnDefault(input);
- }
- bool
- DoFD(char ** input, double * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- *ret = strtod(*input, input);
- return GetReturnDefault(input);
- }
- bool
- DoCD(char ** input, char * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- *ret = **input;
- ++(*input);
- if (*ret == '\\')
- {
- if (IsSpacer(**input))
- {
- // '\ '
- *ret = **input;
- ++(*input);
- }
- else if (**input == '\\')
- {
- // '\\'
- ++(*input);
- }
- }
- return GetReturnDefault(input);
- }
- bool
- DoBD(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- *ret = (int)GetBool(input);
- return GetReturnDefault(input);
- }
- bool
- DoGD(char ** input, double * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- char *
- string = *input;
- int
- temp = 0;
- switch (*string)
- {
- case 'N':
- case 'n':
- if (strichecks(string, "NAN_E"))
- {
- *input += 5;
- temp = FLOAT_NAN_E;
- }
- else if (strichecks(string, "NAN"))
- {
- *input += 3;
- temp = FLOAT_NAN;
- }
- else if (strichecks(string, "NEG_INFINITY"))
- {
- *input += 12;
- temp = FLOAT_NEG_INFINITY;
- }
- else if (strichecks(string, "NEGATIVE_INFINITY"))
- {
- *input += 17;
- temp = FLOAT_NEG_INFINITY;
- }
- *ret = (double)(*((float *)&temp));
- break;
- case 'I':
- case 'i':
- if (strichecks(string, "INFINITY"))
- {
- *input += 8;
- temp = FLOAT_INFINITY;
- *ret = (double)(*((float *)&temp));
- }
- break;
- case '-':
- if (strichecks(string + 1, "INFINITY"))
- {
- *input += 9;
- temp = FLOAT_NEG_INFINITY;
- *ret = (double)(*((float *)&temp));
- break;
- }
- // FALLTHROUGH
- default:
- // Read in the value and save the pointer - may as well use
- // existing, pre set up variables.
- *ret = strtod(string, input);
- break;
- }
- return GetReturnDefault(input);
- }
- bool
- DoSD(char ** input, char ** ret, int * length)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- // Don't reallocate the memory, just set pointers to the current data and
- // add nulls to terminate.
- int
- i = 0;
- // Save the pointer to the start of the data.
- *ret = *input;
- // Check if we want the whole remaining string or just the next word.
- char
- * string = *input,
- * outp = string;
- while (!IsEnd(*string) && !IsDelimiter(*string))
- {
- ++i;
- if (*string == '\\')
- {
- if (IsEnd(*(string + 1)))
- {
- ++string;
- break;
- }
- // Escape spaces, backspace and delimiters - this code is
- // context independent so you can use a string with or
- // without a delimiter and can still escape spaces.
- if (*(string + 1) == '\\' || IsWhitespace(*(string + 1)) || IsDelimiter(*(string + 1)))
- {
- ++string;
- }
- }
- if (outp != string)
- {
- *outp = *string;
- }
- ++outp;
- ++string;
- }
- if (IsDelimiter(*string))
- {
- // Skip the final character.
- *input = string + 1;
- // NOW get the length.
- *length = GetLength(input, false);
- }
- else
- {
- logprintf("sscanf warning: Unclosed default value.");
- // Save the return.
- *input = string;
- logprintf("sscanf warning: Strings without a length are deprecated, please add a destination size.");
- *length = SSCANF_MAX_LENGTH;
- }
- // Add a null terminator.
- if (i >= *length)
- {
- logprintf("sscanf warning: String buffer overflow.");
- *(ret + (*length - 1)) = '\0';
- }
- else
- {
- *outp = '\0';
- }
- return true;
- }
- bool
- DoUD(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- if (gOptions & 1) DoU(input, ret, 0);
- else DoN(input, ret);
- return GetReturnDefault(input);
- }
- bool
- DoQD(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- if (gOptions & 1) DoQ(input, ret, 0);
- else DoN(input, ret);
- return GetReturnDefault(input);
- }
- bool
- DoRD(char ** input, int * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- if (gOptions & 1) DoR(input, ret, 0);
- else DoN(input, ret);
- return GetReturnDefault(input);
- }
- bool
- DoLD(char ** input, bool * ret)
- {
- if (!FindDefaultStart(input))
- {
- return false;
- }
- DoL(input, ret);
- return GetReturnDefault(input);
- }
|