* Fixed a deallocation mistake and some buffer overflows in npc_chat.c after doing rtfm@pcre.txt (all caused by incorrect usage of pcre api)

* Removed underscores in npc_chat.c's variable names (easier to read)
* Reindented the whole thing (used spaces half of the time ._.)
* Moved npc pcre-data deallocation from npc_remove_map to npc_unload

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@11036 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
ultramage 2007-08-19 06:04:34 +00:00
parent 1518e0240b
commit 97e812dda1
6 changed files with 365 additions and 426 deletions

View File

@ -3,6 +3,11 @@ Date Added
AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK. AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
2007/08/18
* Fixed a deallocation mistake and some buffer overflows in npc_chat.c
after doing rtfm@pcre.txt (all caused by incorrect usage of pcre api)
* Removed underscores in npc_chat.c's variable names (easier to read)
* Moved npc pcre-data deallocation from npc_remove_map to npc_unload
2007/08/17 2007/08/17
* Implemented the official dual-wield aspd equation [ultramage] * Implemented the official dual-wield aspd equation [ultramage]
- using 0.7 instead of 0.66 as modifier (so aspd will be lower now) - using 0.7 instead of 0.66 as modifier (so aspd will be lower now)

View File

@ -1,13 +1,13 @@
// Athena charcommand Configuration file. // Athena charcommand Configuration file.
// Translated by Peter Kieser <pfak@telus.net> // Translated by Peter Kieser <pfak@telus.net>
// Set here the symbol that you want to use for your commands // The symbol that will be used to recognize commands.
// Only 1 character is get (default is '#'). You can set any character, // You can set any one character, except control-characters (0x00-0x1f),
// except control-character (0x00-0x1f), '%' (party chat speaking), // '%', '$' (party/guild chat speaking) and '/' (standard client commands).
// '/' (standard ragnarok GM commands) and '@' (Standard GM Commands) // The symbol must be different from from the standard GM command symbol.
// With default character, all commands begin by a '#', example: #save SomePlayer
command_symbol: # command_symbol: #
//-------------------------- //--------------------------
// 0: normal player commands // 0: normal player commands
// None for security purposes. // None for security purposes.
@ -25,85 +25,86 @@ command_symbol: #
// 40: Sub-GM commands // 40: Sub-GM commands
//View the items in a character's cart // View the items in a character's cart
cartlist: 40 cartlist: 40
//Apply an effect onto another character // Apply an effect onto another character
effect: 40 effect: 40
//View the exp of a character // View the exp of a character
exp: 40 exp: 40
//List a chacter's items // List a chacter's items
itemlist: 40 itemlist: 40
//View the jail time remaining // View the jail time remaining
jailtime: 40 jailtime: 40
//Refresh a character // Refresh a character
refresh: 40 refresh: 40
//List a chacter's stats // List a chacter's stats
stats: 40 stats: 40
//List a chacter's storage items // List a chacter's storage items
storagelist: 40 storagelist: 40
//--------------------- //---------------------
// 50: Sub-GM+ commands // 50: Sub-GM+ commands
//Change a character's clothing color // Change a character's clothing color
dye: 50 dye: 50
//Give another character a fake name // Give another character a fake name
fakename: 50 fakename: 50
//Open the hatch dialog for a character // Open the hatch dialog for a character
hatch: 50 hatch: 50
//Change a character's hair color // Change a character's hair color
hcolor: 50 hcolor: 50
haircolor: 50 haircolor: 50
//Change a character's hair style // Change a character's hair style
hstyle: 50 hstyle: 50
hairstyle: 50 hairstyle: 50
// Changes character's model // Changes character's model
model: 50 model: 50
//Give or remove a peco from a character // Give or remove a peco from a character
mount: 50 mount: 50
mountpeco: 50 mountpeco: 50
//Make another character's pet friendly/not // Make another character's pet friendly/not
petfriendly: 50 petfriendly: 50
//Rename another character's pet // Rename another character's pet
petrename: 50 petrename: 50
//Change a character's size // Change a character's size
size: 50 size: 50
//---------------- //----------------
// 60: GM commands // 60: GM commands
//Resurrects a dead character // Resurrects a dead character
alive: 60 alive: 60
revive: 60
//Give a player all the skills available to him/her // Give a player all the skills available to him/her
allskill: 60 allskill: 60
allskills: 60 allskills: 60
skillall: 60 skillall: 60
skillsall: 60 skillsall: 60
//Give a character the maximum possible stats // Give a character the maximum possible stats
allstat: 60 allstat: 60
allstats: 60 allstats: 60
statall: 60 statall: 60
statsall: 60 statsall: 60
//Change another character's base level (3 same commands) // Change another character's base level (3 same commands)
blvl: 60 blvl: 60
blevel: 60 blevel: 60
baselvl: 60 baselvl: 60
@ -112,118 +113,118 @@ baselevel: 60
// Changes the sex of an online player (all characters on the account) // Changes the sex of an online player (all characters on the account)
changesex: 60 changesex: 60
//Delete items from a character // Delete items from a character
delitem: 60 delitem: 60
//Disguise a character // Disguise a character
disguise: 60 disguise: 60
undisguise: 60 undisguise: 60
//Resets another character's designated maps // Resets another character's designated maps
feelreset: 60 feelreset: 60
//Change the guild level for a character's guild // Change the guild level for a character's guild
glvl: 60 glvl: 60
glevel: 60 glevel: 60
guildlvl: 60 guildlvl: 60
guildlevel: 60 guildlevel: 60
//Open guild storage for a character // Open guild storage for a character
gstorage: 60 gstorage: 60
//Heal a character // Heal a character
heal: 60 heal: 60
//Invoke GM Hide on a character // Invoke GM Hide on a character
hide: 60 hide: 60
//Increase a character's homunculus' level // Increase a character's homunculus' level
hlvl: 60 hlvl: 60
hlevel: 60 hlevel: 60
homlvl: 60 homlvl: 60
homlevel: 60 homlevel: 60
//Evolve a character's homunculus // Evolve a character's homunculus
homevolve: 60 homevolve: 60
homevolution: 60 homevolution: 60
//Change a character's homunculus' friendly value // Change a character's homunculus' friendly value
homfriendly: 60 homfriendly: 60
//Change a character's homunculus' hunger value // Change a character's homunculus' hunger value
homhungry: 60 homhungry: 60
//View a character's homunculus' stats // View a character's homunculus' stats
hominfo: 40 hominfo: 40
//Give another character an item // Give another character an item
item: 60 item: 60
//Create a specific item (with cards, refines, etc // Create a specific item (with cards, refines, etc
item2: 60 item2: 60
//Remove all of a character's possessions // Remove all of a character's possessions
itemreset: 60 itemreset: 60
//Change another character's job (2 same commands) // Change another character's job (2 same commands)
job: 60 job: 60
jobchange: 60 jobchange: 60
//Change another character's job level (3 same commands) // Change another character's job level (3 same commands)
jlvl: 60 jlvl: 60
jlevel: 60 jlevel: 60
joblvl: 60 joblvl: 60
joblevel: 60 joblevel: 60
//Allow a character to attack anybody // Allow a character to attack anybody
killer: 60 killer: 60
//Make a character killable by anybody // Make a character killable by anybody
killable: 60 killable: 60
//Return a character to their respawn point // Return a character to their respawn point
load: 60 load: 60
//Take away a character's platinum skill // Take away a character's platinum skill
lostskill: 60 lostskill: 60
//Make a character immune to monsters // Make a character immune to monsters
monsterignore: 60 monsterignore: 60
//Apply a certain option to another character // Apply a certain option to another character
option: 60 option: 60
//Change a character's pet's hungry value // Change a character's pet's hungry value
pethungry: 60 pethungry: 60
//Produce forged equipment on a character (as if he/she was a blacksmith) // Produce forged equipment on a character (as if he/she was a blacksmith)
produce: 60 produce: 60
//Give another character a platinum skill // Give another character a platinum skill
questskill: 60 questskill: 60
//Performs a stat and skill reset on someone else. // Performs a stat and skill reset on someone else.
reset: 60 reset: 60
//Save another character // Save another character
save: 60 save: 60
// Gives another character skill points // Gives another character skill points
skpoint: 60 skpoint: 60
//Change a character's walking speed // Change a character's walking speed
speed: 60 speed: 60
//Give another character spiritball effect // Give another character spiritball effect
spiritball: 60 spiritball: 60
//Open storage for a character // Open storage for a character
storage: 60 storage: 60
// Gives another character status points // Gives another character status points
stpoint: 60 stpoint: 60
//Change a character's stats // Change a character's stats
str: 60 str: 60
agi: 60 agi: 60
vit: 60 vit: 60
@ -243,10 +244,10 @@ rura+: 60
//---------------------- //----------------------
// 80: GM Chief commands // 80: GM Chief commands
//Refine a character's equipment // Refine a character's equipment
refine: 80 refine: 80
//Give another character zeny // Give another character zeny
zeny: 80 zeny: 80
//--------------------------- //---------------------------

View File

@ -844,7 +844,7 @@ struct npc_data {
int eventtimer[MAX_EVENTTIMER]; int eventtimer[MAX_EVENTTIMER];
short arenaflag; short arenaflag;
void *chatdb; void* chatdb; // pointer to a npc_parse struct (see npc_chat.c)
struct npc_data *master_nd; struct npc_data *master_nd;
union { union {

View File

@ -1441,9 +1441,6 @@ int npc_remove_map(struct npc_data* nd)
if(nd->bl.prev == NULL || nd->bl.m < 0) if(nd->bl.prev == NULL || nd->bl.m < 0)
return 1; //Not assigned to a map. return 1; //Not assigned to a map.
m = nd->bl.m; m = nd->bl.m;
#ifdef PCRE_SUPPORT
npc_chat_finalize(nd);
#endif
clif_clearunit_area(&nd->bl,2); clif_clearunit_area(&nd->bl,2);
//Remove corresponding NPC CELLs //Remove corresponding NPC CELLs
if (nd->bl.subtype == WARP) { if (nd->bl.subtype == WARP) {
@ -1513,6 +1510,10 @@ int npc_unload(struct npc_data* nd)
if (nd->chat_id) // remove npc chatroom object and kick users if (nd->chat_id) // remove npc chatroom object and kick users
chat_deletenpcchat(nd); chat_deletenpcchat(nd);
#ifdef PCRE_SUPPORT
npc_chat_finalize(nd); // deallocate npc PCRE data structures
#endif
if (nd->bl.subtype == SCRIPT) { if (nd->bl.subtype == SCRIPT) {
ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related. ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related.
if (nd->u.scr.timerid != -1) { if (nd->u.scr.timerid != -1) {

View File

@ -4,6 +4,8 @@
#ifndef _NPC_H_ #ifndef _NPC_H_
#define _NPC_H_ #define _NPC_H_
#include "map.h" // TBL_NPC
#define START_NPC_NUM 110000000 #define START_NPC_NUM 110000000
#define WARP_CLASS 45 #define WARP_CLASS 45

View File

@ -3,26 +3,21 @@
#ifdef PCRE_SUPPORT #ifdef PCRE_SUPPORT
#include "../common/timer.h"
#include "../common/malloc.h"
#include "../common/nullpo.h"
#include "../common/showmsg.h"
#include "../common/strlib.h"
#include "map.h" // struct mob_data, struct npc_data
#include "script.h" // set_var()
#include "pcre.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <time.h>
#include "../common/timer.h"
#include "../common/malloc.h"
#include "../common/version.h"
#include "../common/nullpo.h"
#include "../common/showmsg.h"
#include "map.h"
#include "status.h"
#include "npc.h"
#include "chat.h"
#include "script.h"
#include "battle.h"
#include "pcre.h"
/** /**
* Written by MouseJstr in a vision... (2/21/2005) * Written by MouseJstr in a vision... (2/21/2005)
@ -71,24 +66,21 @@
* deletes a pset * deletes a pset
*/ */
/* Structure containing all info associated with a single pattern /* Structure containing all info associated with a single pattern block */
block */
struct pcrematch_entry { struct pcrematch_entry {
struct pcrematch_entry *next_; struct pcrematch_entry* next;
char *pattern_; char* pattern;
pcre *pcre_; pcre* pcre;
pcre_extra *pcre_extra_; pcre_extra* pcre_extra;
char *label_; char* label;
}; };
/* A set of patterns that can be activated and deactived with a single /* A set of patterns that can be activated and deactived with a single command */
command */
struct pcrematch_set { struct pcrematch_set {
struct pcrematch_set *next_, *prev_; struct pcrematch_set* prev;
struct pcrematch_entry *head_; struct pcrematch_set* next;
int setid_; struct pcrematch_entry* head;
int setid;
}; };
/* /*
@ -100,10 +92,9 @@ struct pcrematch_set {
* also wanted people to be able to grab this one file to get updates * also wanted people to be able to grab this one file to get updates
* without having to do a large number of changes. * without having to do a large number of changes.
*/ */
struct npc_parse { struct npc_parse {
struct pcrematch_set *active_; struct pcrematch_set* active;
struct pcrematch_set *inactive_; struct pcrematch_set* inactive;
}; };
@ -112,60 +103,48 @@ struct npc_parse {
* *
* This does NOT do the list management * This does NOT do the list management
*/ */
void finalize_pcrematch_entry(struct pcrematch_entry* e)
void finalize_pcrematch_entry(struct pcrematch_entry *e)
{ {
//TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex] pcre_free(e->pcre);
#ifndef _WIN32 pcre_free(e->pcre_extra);
if (e->pcre_) { aFree(e->pattern);
free(e->pcre_); aFree(e->label);
e->pcre_ = NULL;
}
#endif
if (e->pcre_extra_) {
free(e->pcre_extra_);
e->pcre_ = NULL;
}
aFree(e->pattern_);
aFree(e->label_);
} }
/** /**
* Lookup (and possibly create) a new set of patterns by the set id * Lookup (and possibly create) a new set of patterns by the set id
*/ */
static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid) static struct pcrematch_set* lookup_pcreset(struct npc_data* nd, int setid)
{ {
struct pcrematch_set *pcreset; struct pcrematch_set *pcreset;
struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
if (npcParse == NULL) if (npcParse == NULL)
nd->chatdb = npcParse = (struct npc_parse *) nd->chatdb = npcParse = (struct npc_parse *) aCalloc(sizeof(struct npc_parse), 1);
aCalloc(sizeof(struct npc_parse), 1);
pcreset = npcParse->active_; pcreset = npcParse->active;
while (pcreset != NULL) { while (pcreset != NULL) {
if (pcreset->setid_ == setid) if (pcreset->setid == setid)
break; break;
pcreset = pcreset->next_; pcreset = pcreset->next;
} }
if (pcreset == NULL) if (pcreset == NULL)
pcreset = npcParse->inactive_; pcreset = npcParse->inactive;
while (pcreset != NULL) { while (pcreset != NULL) {
if (pcreset->setid_ == setid) if (pcreset->setid == setid)
break; break;
pcreset = pcreset->next_; pcreset = pcreset->next;
} }
if (pcreset == NULL) { if (pcreset == NULL) {
pcreset = (struct pcrematch_set *) pcreset = (struct pcrematch_set *) aCalloc(sizeof(struct pcrematch_set), 1);
aCalloc(sizeof(struct pcrematch_set), 1); pcreset->next = npcParse->inactive;
pcreset->next_ = npcParse->inactive_; if (pcreset->next != NULL)
if (pcreset->next_ != NULL) pcreset->next->prev = pcreset;
pcreset->next_->prev_ = pcreset; pcreset->prev = 0;
pcreset->prev_ = 0; npcParse->inactive = pcreset;
npcParse->inactive_ = pcreset; pcreset->setid = setid;
pcreset->setid_ = setid;
} }
return pcreset; return pcreset;
@ -176,33 +155,32 @@ static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid)
* *
* if the setid does not exist, this will silently return * if the setid does not exist, this will silently return
*/ */
static void activate_pcreset(struct npc_data* nd, int setid)
static void activate_pcreset(struct npc_data *nd,int setid)
{ {
struct pcrematch_set *pcreset; struct pcrematch_set *pcreset;
struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
if (npcParse == NULL) if (npcParse == NULL)
return; // Nothing to activate... return; // Nothing to activate...
pcreset = npcParse->inactive_; pcreset = npcParse->inactive;
while (pcreset != NULL) { while (pcreset != NULL) {
if (pcreset->setid_ == setid) if (pcreset->setid == setid)
break; break;
pcreset = pcreset->next_; pcreset = pcreset->next;
} }
if (pcreset == NULL) if (pcreset == NULL)
return; // not in inactive list return; // not in inactive list
if (pcreset->next_ != NULL) if (pcreset->next != NULL)
pcreset->next_->prev_ = pcreset->prev_; pcreset->next->prev = pcreset->prev;
if (pcreset->prev_ != NULL) if (pcreset->prev != NULL)
pcreset->prev_->next_ = pcreset->next_; pcreset->prev->next = pcreset->next;
else else
npcParse->inactive_ = pcreset->next_; npcParse->inactive = pcreset->next;
pcreset->prev_ = NULL; pcreset->prev = NULL;
pcreset->next_ = npcParse->active_; pcreset->next = npcParse->active;
if (pcreset->next_ != NULL) if (pcreset->next != NULL)
pcreset->next_->prev_ = pcreset; pcreset->next->prev = pcreset;
npcParse->active_ = pcreset; npcParse->active = pcreset;
} }
/** /**
@ -210,86 +188,85 @@ static void activate_pcreset(struct npc_data *nd,int setid)
* *
* if the setid does not exist, this will silently return * if the setid does not exist, this will silently return
*/ */
static void deactivate_pcreset(struct npc_data* nd, int setid)
static void deactivate_pcreset(struct npc_data *nd,int setid)
{ {
struct pcrematch_set *pcreset; struct pcrematch_set *pcreset;
struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
if (npcParse == NULL) if (npcParse == NULL)
return; // Nothing to deactivate... return; // Nothing to deactivate...
if (setid == -1) { if (setid == -1) {
while(npcParse->active_ != NULL) while(npcParse->active != NULL)
deactivate_pcreset(nd, npcParse->active_->setid_); deactivate_pcreset(nd, npcParse->active->setid);
return; return;
} }
pcreset = npcParse->active_; pcreset = npcParse->active;
while (pcreset != NULL) { while (pcreset != NULL) {
if (pcreset->setid_ == setid) if (pcreset->setid == setid)
break; break;
pcreset = pcreset->next_; pcreset = pcreset->next;
} }
if (pcreset == NULL) if (pcreset == NULL)
return; // not in active list return; // not in active list
if (pcreset->next_ != NULL) if (pcreset->next != NULL)
pcreset->next_->prev_ = pcreset->prev_; pcreset->next->prev = pcreset->prev;
if (pcreset->prev_ != NULL) if (pcreset->prev != NULL)
pcreset->prev_->next_ = pcreset->next_; pcreset->prev->next = pcreset->next;
else else
npcParse->active_ = pcreset->next_; npcParse->active = pcreset->next;
pcreset->prev_ = NULL; pcreset->prev = NULL;
pcreset->next_ = npcParse->inactive_; pcreset->next = npcParse->inactive;
if (pcreset->next_ != NULL) if (pcreset->next != NULL)
pcreset->next_->prev_ = pcreset; pcreset->next->prev = pcreset;
npcParse->inactive_ = pcreset; npcParse->inactive = pcreset;
} }
/** /**
* delete a set of patterns. * delete a set of patterns.
*/ */
static void delete_pcreset(struct npc_data *nd,int setid) static void delete_pcreset(struct npc_data* nd, int setid)
{ {
int active = 1; int active = 1;
struct pcrematch_set *pcreset; struct pcrematch_set *pcreset;
struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
if (npcParse == NULL) if (npcParse == NULL)
return; // Nothing to deactivate... return; // Nothing to deactivate...
pcreset = npcParse->active_; pcreset = npcParse->active;
while (pcreset != NULL) { while (pcreset != NULL) {
if (pcreset->setid_ == setid) if (pcreset->setid == setid)
break; break;
pcreset = pcreset->next_; pcreset = pcreset->next;
} }
if (pcreset == NULL) { if (pcreset == NULL) {
active = 0; active = 0;
pcreset = npcParse->inactive_; pcreset = npcParse->inactive;
while (pcreset != NULL) { while (pcreset != NULL) {
if (pcreset->setid_ == setid) if (pcreset->setid == setid)
break; break;
pcreset = pcreset->next_; pcreset = pcreset->next;
} }
} }
if (pcreset == NULL) if (pcreset == NULL)
return; return;
if (pcreset->next_ != NULL) if (pcreset->next != NULL)
pcreset->next_->prev_ = pcreset->prev_; pcreset->next->prev = pcreset->prev;
if (pcreset->prev_ != NULL) if (pcreset->prev != NULL)
pcreset->prev_->next_ = pcreset->next_; pcreset->prev->next = pcreset->next;
if(active) if(active)
npcParse->active_ = pcreset->next_; npcParse->active = pcreset->next;
else else
npcParse->inactive_ = pcreset->next_; npcParse->inactive = pcreset->next;
pcreset->prev_ = NULL; pcreset->prev = NULL;
pcreset->next_ = NULL; pcreset->next = NULL;
while (pcreset->head_) { while (pcreset->head) {
struct pcrematch_entry *n = pcreset->head_->next_; struct pcrematch_entry* n = pcreset->head->next;
finalize_pcrematch_entry(pcreset->head_); finalize_pcrematch_entry(pcreset->head);
aFree(pcreset->head_); // Cleanin' the last ones.. [Lance] aFree(pcreset->head); // Cleanin' the last ones.. [Lance]
pcreset->head_ = n; pcreset->head = n;
} }
aFree(pcreset); aFree(pcreset);
@ -298,30 +275,28 @@ static void delete_pcreset(struct npc_data *nd,int setid)
/** /**
* create a new pattern entry * create a new pattern entry
*/ */
static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) static struct pcrematch_entry* create_pcrematch_entry(struct pcrematch_set* set)
{ {
struct pcrematch_entry * e = (struct pcrematch_entry *) struct pcrematch_entry * e = (struct pcrematch_entry *) aCalloc(sizeof(struct pcrematch_entry), 1);
aCalloc(sizeof(struct pcrematch_entry), 1); struct pcrematch_entry * last = set->head;
struct pcrematch_entry * last = set->head_;
// Normally we would have just stuck it at the end of the list but // Normally we would have just stuck it at the end of the list but
// this doesn't sink up with peoples usage pattern. They wanted // this doesn't sink up with peoples usage pattern. They wanted
// the items defined first to have a higher priority then the // the items defined first to have a higher priority then the
// items defined later.. as a result, we have to do some work up // items defined later. as a result, we have to do some work up front.
// front..
/* if we are the first pattern, stick us at the end */ /* if we are the first pattern, stick us at the end */
if (last == NULL) { if (last == NULL) {
set->head_ = e; set->head = e;
return e; return e;
} }
/* Look for the last entry */ /* Look for the last entry */
while (last->next_ != NULL) while (last->next != NULL)
last = last->next_; last = last->next;
last->next_ = e; last->next = e;
e->next_ = NULL; e->next = NULL;
return e; return e;
} }
@ -329,19 +304,17 @@ static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set
/** /**
* define/compile a new pattern * define/compile a new pattern
*/ */
void npc_chat_def_pattern(struct npc_data* nd, int setid, const char* pattern, const char* label)
void npc_chat_def_pattern(struct npc_data *nd, int setid,
const char *pattern, const char *label)
{ {
const char *err; const char *err;
int erroff; int erroff;
struct pcrematch_set * s = lookup_pcreset(nd, setid); struct pcrematch_set * s = lookup_pcreset(nd, setid);
struct pcrematch_entry *e = create_pcrematch_entry(s); struct pcrematch_entry *e = create_pcrematch_entry(s);
e->pattern_ = aStrdup(pattern); e->pattern = aStrdup(pattern);
e->label_ = aStrdup(label); e->label = aStrdup(label);
e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL); e->pcre = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL);
e->pcre_extra_ = pcre_study(e->pcre_, 0, &err); e->pcre_extra = pcre_study(e->pcre, 0, &err);
} }
/** /**
@ -350,17 +323,17 @@ void npc_chat_def_pattern(struct npc_data *nd, int setid,
* *
* this could be more efficent but.. how often do you do this? * this could be more efficent but.. how often do you do this?
*/ */
void npc_chat_finalize(struct npc_data *nd) void npc_chat_finalize(struct npc_data* nd)
{ {
struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
if (npcParse == NULL) if (npcParse == NULL)
return; return;
while(npcParse->active_) while(npcParse->active)
delete_pcreset(nd, npcParse->active_->setid_); delete_pcreset(nd, npcParse->active->setid);
while(npcParse->inactive_) while(npcParse->inactive)
delete_pcreset(nd, npcParse->inactive_->setid_); delete_pcreset(nd, npcParse->inactive->setid);
// Additional cleaning up [Lance] // Additional cleaning up [Lance]
aFree(npcParse); aFree(npcParse);
@ -369,154 +342,111 @@ void npc_chat_finalize(struct npc_data *nd)
/** /**
* Handler called whenever a global message is spoken in a NPC's area * Handler called whenever a global message is spoken in a NPC's area
*/ */
int npc_chat_sub(struct block_list *bl, va_list ap) int npc_chat_sub(struct block_list* bl, va_list ap)
{ {
struct npc_data *nd = (struct npc_data *)bl; struct npc_data* nd = (struct npc_data *) bl;
struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; struct npc_parse* npcParse = (struct npc_parse *) nd->chatdb;
char *msg; char* msg;
int len, pos, i; int len, i;
struct map_session_data *sd; struct map_session_data* sd;
struct npc_label_list *lst; struct npc_label_list* lst;
struct pcrematch_set *pcreset; struct pcrematch_set* pcreset;
struct pcrematch_entry* e;
// Not interested in anything you might have to say... // Not interested in anything you might have to say...
if (npcParse == NULL || npcParse->active_ == NULL) if (npcParse == NULL || npcParse->active == NULL)
return 0; return 0;
msg = va_arg(ap,char*); msg = va_arg(ap,char*);
len = va_arg(ap,int); len = va_arg(ap,int);
sd = va_arg(ap,struct map_session_data *); sd = va_arg(ap,struct map_session_data *);
// grab the active list // iterate across all active sets
pcreset = npcParse->active_; for (pcreset = npcParse->active; pcreset != NULL; pcreset = pcreset->next)
{
// interate across all active sets
while (pcreset != NULL) {
struct pcrematch_entry *e = pcreset->head_;
// interate across all patterns in that set // interate across all patterns in that set
while (e != NULL) { for (e = pcreset->head; e != NULL; e = e->next)
int offsets[20]; {
char buf[255]; int offsets[2*10 + 10]; // 1/3 reserved for temp space requred by pcre_exec
// perform pattern match // perform pattern match
int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0, int r = pcre_exec(e->pcre, e->pcre_extra, msg, len, 0, 0, offsets, ARRAYLENGTH(offsets));
0, offsets, sizeof(offsets) / sizeof(offsets[0])); if (r > 0)
if (r >= 0) { {
// save out the matched strings // save out the matched strings
switch (r) { for (i = 0; i < r; i++)
case 10: {
memcpy(buf, &msg[offsets[18]], offsets[19]); char var[6], val[255];
buf[offsets[19]] = '\0'; snprintf(var, sizeof(var), "$@p%i$", i);
set_var(sd, "$@p9$", buf); pcre_copy_substring(msg, offsets, r, i, val, sizeof(val));
case 9: set_var(sd, var, val);
memcpy(buf, &msg[offsets[16]], offsets[17]);
buf[offsets[17]] = '\0';
set_var(sd, "$@p8$", buf);
case 8:
memcpy(buf, &msg[offsets[14]], offsets[15]);
buf[offsets[15]] = '\0';
set_var(sd, "$@p7$", buf);
case 7:
memcpy(buf, &msg[offsets[12]], offsets[13]);
buf[offsets[13]] = '\0';
set_var(sd, "$@p6$", buf);
case 6:
memcpy(buf, &msg[offsets[10]], offsets[11]);
buf[offsets[11]] = '\0';
set_var(sd, "$@p5$", buf);
case 5:
memcpy(buf, &msg[offsets[8]], offsets[9]);
buf[offsets[9]] = '\0';
set_var(sd, "$@p4$", buf);
case 4:
memcpy(buf, &msg[offsets[6]], offsets[7]);
buf[offsets[7]] = '\0';
set_var(sd, "$@p3$", buf);
case 3:
memcpy(buf, &msg[offsets[4]], offsets[5]);
buf[offsets[5]] = '\0';
set_var(sd, "$@p2$", buf);
case 2:
memcpy(buf, &msg[offsets[2]], offsets[3]);
buf[offsets[3]] = '\0';
set_var(sd, "$@p1$", buf);
case 1:
memcpy(buf, &msg[offsets[0]], offsets[1]);
buf[offsets[1]] = '\0';
set_var(sd, "$@p0$", buf);
} }
// find the target label.. this sucks.. // find the target label.. this sucks..
lst=nd->u.scr.label_list; lst = nd->u.scr.label_list;
pos = -1; ARR_FIND(0, nd->u.scr.label_list_num, i, strncmp(lst[i].name, e->label, sizeof(lst[i].name)) == 0);
for (i = 0; i < nd->u.scr.label_list_num; i++) { if (i == nd->u.scr.label_list_num) {
if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) { ShowWarning("Unable to find label: %s", e->label);
pos = lst[i].pos;
break;
}
}
if (pos == -1) {
ShowWarning("Unable to find label: %s", e->label_);
// unable to find label... do something..
return 0; return 0;
} }
// run the npc script // run the npc script
run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id); run_script(nd->u.scr.script,lst[i].pos,sd->bl.id,nd->bl.id);
return 0; return 0;
} }
e = e->next_;
} }
pcreset = pcreset->next_;
} }
return 0; return 0;
} }
int mob_chat_sub(struct block_list *bl, va_list ap){ int mob_chat_sub(struct block_list* bl, va_list ap)
{
struct mob_data *md = (struct mob_data *)bl; struct mob_data *md = (struct mob_data *)bl;
if(md->nd){ if(md->nd)
npc_chat_sub(&md->nd->bl, ap); npc_chat_sub(&md->nd->bl, ap);
}
return 0; return 0;
} }
// Various script builtins used to support these functions // Various script builtins used to support these functions
int buildin_defpattern(struct script_state *st) int buildin_defpattern(struct script_state* st)
{ {
int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); int setid = conv_num(st,& (st->stack->stack_data[st->start+2]));
const char *pattern=conv_str(st,& (st->stack->stack_data[st->start+3])); const char* pattern = conv_str(st,& (st->stack->stack_data[st->start+3]));
const char *label=conv_str(st,& (st->stack->stack_data[st->start+4])); const char* label = conv_str(st,& (st->stack->stack_data[st->start+4]));
struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid);
npc_chat_def_pattern(nd, setid, pattern, label); npc_chat_def_pattern(nd, setid, pattern, label);
return 0; return 0;
} }
int buildin_activatepset(struct script_state *st) int buildin_activatepset(struct script_state* st)
{ {
int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); int setid = conv_num(st,& (st->stack->stack_data[st->start+2]));
struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid);
activate_pcreset(nd, setid); activate_pcreset(nd, setid);
return 0; return 0;
} }
int buildin_deactivatepset(struct script_state *st) int buildin_deactivatepset(struct script_state* st)
{ {
int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); int setid = conv_num(st,& (st->stack->stack_data[st->start+2]));
struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid);
deactivate_pcreset(nd, setid); deactivate_pcreset(nd, setid);
return 0; return 0;
} }
int buildin_deletepset(struct script_state *st) int buildin_deletepset(struct script_state* st)
{ {
int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); int setid = conv_num(st,& (st->stack->stack_data[st->start+2]));
struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid);
delete_pcreset(nd, setid); delete_pcreset(nd, setid);