summaryrefslogtreecommitdiff
path: root/plugins/!NotAdopted/VypressChat/contrib
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2013-04-02 13:54:21 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2013-04-02 13:54:21 +0000
commitff5a775b94465b30897964630af600fe5915fc51 (patch)
treef79e36c79faf7095ca6f02f6b7fea0f6e81bf085 /plugins/!NotAdopted/VypressChat/contrib
parent8d3307adf7ba64b75fb4de363f873c97286b0e9b (diff)
VypressChat added (not adopted)
git-svn-id: http://svn.miranda-ng.org/main/trunk@4285 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/!NotAdopted/VypressChat/contrib')
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/hashtable.c287
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/hashtable.h195
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h71
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/m_chat.h424
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/memwatch.c2664
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/memwatch.h707
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c21
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h8
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/unicows.dllbin0 -> 258352 bytes
9 files changed, 4377 insertions, 0 deletions
diff --git a/plugins/!NotAdopted/VypressChat/contrib/hashtable.c b/plugins/!NotAdopted/VypressChat/contrib/hashtable.c
new file mode 100644
index 0000000000..5bc1b2f539
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/hashtable.c
@@ -0,0 +1,287 @@
+/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#include "hashtable.h"
+#include "hashtable_private.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef MEMWATCH
+# include "../contrib/memwatch.h"
+#endif
+
+/*
+Credit for primes table: Aaron Krowne
+ http://br.endernet.org/~akrowne/
+ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
+*/
+static const unsigned int primes[] = {
+53, 97, 193, 389,
+769, 1543, 3079, 6151,
+12289, 24593, 49157, 98317,
+196613, 393241, 786433, 1572869,
+3145739, 6291469, 12582917, 25165843,
+50331653, 100663319, 201326611, 402653189,
+805306457, 1610612741
+};
+const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
+const float max_load_factor = 0.65;
+
+/*****************************************************************************/
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashf) (void*),
+ int (*eqf) (void*,void*),
+ void (*value_free_fn)(void *))
+{
+ struct hashtable *h;
+ unsigned int pindex, size = primes[0];
+ /* Check requested hashtable isn't too large */
+ if (minsize > (1u << 30)) return NULL;
+ /* Enforce size as prime */
+ for (pindex=0; pindex < prime_table_length; pindex++) {
+ if (primes[pindex] > minsize) { size = primes[pindex]; break; }
+ }
+ h = (struct hashtable *)malloc(sizeof(struct hashtable));
+ if (NULL == h) return NULL; /*oom*/
+ h->table = (struct entry **)malloc(sizeof(struct entry*) * size);
+ if (NULL == h->table) { free(h); return NULL; } /*oom*/
+ memset(h->table, 0, size * sizeof(struct entry *));
+ h->tablelength = size;
+ h->primeindex = pindex;
+ h->entrycount = 0;
+ h->hashfn = hashf;
+ h->eqfn = eqf;
+ h->value_free_fn = value_free_fn;
+ h->loadlimit = (unsigned int) ceil(size * max_load_factor);
+ return h;
+}
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k)
+{
+ /* Aim to protect against poor hash functions by adding logic here
+ * - logic taken from java 1.4 hashtable source */
+ unsigned int i = h->hashfn(k);
+ i += ~(i << 9);
+ i ^= ((i >> 14) | (i << 18)); /* >>> */
+ i += (i << 4);
+ i ^= ((i >> 10) | (i << 22)); /* >>> */
+ return i;
+}
+
+/*****************************************************************************/
+static int
+hashtable_expand(struct hashtable *h)
+{
+ /* Double the size of the table to accomodate more entries */
+ struct entry **newtable;
+ struct entry *e;
+ struct entry **pE;
+ unsigned int newsize, i, index;
+ /* Check we're not hitting max capacity */
+ if (h->primeindex == (prime_table_length - 1)) return 0;
+ newsize = primes[++(h->primeindex)];
+
+ newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize);
+ if (NULL != newtable)
+ {
+ memset(newtable, 0, newsize * sizeof(struct entry *));
+ /* This algorithm is not 'stable'. ie. it reverses the list
+ * when it transfers entries between the tables */
+ for (i = 0; i < h->tablelength; i++) {
+ while (NULL != (e = h->table[i])) {
+ h->table[i] = e->next;
+ index = indexFor(newsize,e->h);
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ free(h->table);
+ h->table = newtable;
+ }
+ /* Plan B: realloc instead */
+ else
+ {
+ newtable = (struct entry **)
+ realloc(h->table, newsize * sizeof(struct entry *));
+ if (NULL == newtable) { (h->primeindex)--; return 0; }
+ h->table = newtable;
+ memset(newtable[h->tablelength], 0, newsize - h->tablelength);
+ for (i = 0; i < h->tablelength; i++) {
+ for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
+ index = indexFor(newsize,e->h);
+ if (index == i)
+ {
+ pE = &(e->next);
+ }
+ else
+ {
+ *pE = e->next;
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ }
+ }
+ h->tablelength = newsize;
+ h->loadlimit = (unsigned int) ceil(newsize * max_load_factor);
+ return -1;
+}
+
+/*****************************************************************************/
+unsigned int
+hashtable_count(struct hashtable *h)
+{
+ return h->entrycount;
+}
+
+/*****************************************************************************/
+int
+hashtable_insert(struct hashtable *h, void *k, void *v)
+{
+ /* This method allows duplicate keys - but they shouldn't be used */
+ unsigned int index;
+ struct entry *e;
+ if (++(h->entrycount) > h->loadlimit)
+ {
+ /* Ignore the return value. If expand fails, we should
+ * still try cramming just this value into the existing table
+ * -- we may not have memory for a larger table, but one more
+ * element may be ok. Next time we insert, we'll try expanding again.*/
+ hashtable_expand(h);
+ }
+ e = (struct entry *)malloc(sizeof(struct entry));
+ if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
+ e->h = hash(h,k);
+ index = indexFor(h->tablelength,e->h);
+ e->k = k;
+ e->v = v;
+ e->next = h->table[index];
+ h->table[index] = e;
+ return -1;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_search(struct hashtable *h, void *k)
+{
+ struct entry *e;
+ unsigned int hashvalue, index;
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hashvalue);
+ e = h->table[index];
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v;
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_remove(struct hashtable *h, void *k, int free_value)
+{
+ /* TODO: consider compacting the table when the load factor drops enough,
+ * or provide a 'compact' method. */
+
+ struct entry *e;
+ struct entry **pE;
+ void *v;
+ unsigned int hashvalue, index;
+
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hash(h,k));
+ pE = &(h->table[index]);
+ e = *pE;
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
+ {
+ *pE = e->next;
+ h->entrycount--;
+ v = e->v;
+ freekey(e->k);
+ free(e);
+
+ if(h->value_free_fn && free_value) {
+ h->value_free_fn(v);
+ v = NULL;
+ }
+
+ return v;
+ }
+ pE = &(e->next);
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+/* destroy */
+void
+hashtable_destroy(struct hashtable *h, int free_values)
+{
+ unsigned int i;
+ struct entry *e, *f;
+ struct entry **table = h->table;
+ if (free_values)
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k);
+ if(h->value_free_fn) h->value_free_fn(f->v);
+ free(f); }
+ }
+ }
+ else
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k); free(f); }
+ }
+ }
+ free(h->table);
+ free(h);
+}
+
+void hashtable_enumerate(
+ struct hashtable * h,
+ hashtable_enum_fn enum_fn, void * user_data)
+{
+ unsigned int i;
+ struct entry * e;
+
+ for(i = 0; i < h->tablelength; i++) {
+ for(e = h->table[i]; e; e = e->next)
+ if(!enum_fn(e->k, e->v, user_data))
+ return;
+ }
+}
+
+/*
+ * Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/hashtable.h b/plugins/!NotAdopted/VypressChat/contrib/hashtable.h
new file mode 100644
index 0000000000..bee0da25e7
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/hashtable.h
@@ -0,0 +1,195 @@
+/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#ifndef __HASHTABLE_CWC22_H__
+#define __HASHTABLE_CWC22_H__
+
+struct hashtable;
+
+/* Example of use:
+ *
+ * struct hashtable *h;
+ * struct some_key *k;
+ * struct some_value *v;
+ *
+ * static unsigned int hash_from_key_fn( void *k );
+ * static int keys_equal_fn ( void *key1, void *key2 );
+ *
+ * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn, value_free_fn);
+ * k = (struct some_key *) malloc(sizeof(struct some_key));
+ * v = (struct some_value *) malloc(sizeof(struct some_value));
+ *
+ * (initialise k and v to suitable values)
+ *
+ * if (! hashtable_insert(h,k,v) )
+ * { exit(-1); }
+ *
+ * if (NULL == (found = hashtable_search(h,k) ))
+ * { printf("not found!"); }
+ *
+ * if (NULL == (found = hashtable_remove(h,k) ))
+ * { printf("Not found\n"); }
+ *
+ */
+
+/* Macros may be used to define type-safe(r) hashtable access functions, with
+ * methods specialized to take known key and value types as parameters.
+ *
+ * Example:
+ *
+ * Insert this at the start of your file:
+ *
+ * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value);
+ *
+ * This defines the functions 'insert_some', 'search_some' and 'remove_some'.
+ * These operate just like hashtable_insert etc., with the same parameters,
+ * but their function signatures have 'struct some_key *' rather than
+ * 'void *', and hence can generate compile time errors if your program is
+ * supplying incorrect data as a key (and similarly for value).
+ *
+ * Note that the hash and key equality functions passed to create_hashtable
+ * still take 'void *' parameters instead of 'some key *'. This shouldn't be
+ * a difficult issue as they're only defined and passed once, and the other
+ * functions will ensure that only valid keys are supplied to them.
+ *
+ * The cost for this checking is increased code size and runtime overhead
+ * - if performance is important, it may be worth switching back to the
+ * unsafe methods once your program has been debugged with the safe methods.
+ * This just requires switching to some simple alternative defines - eg:
+ * #define insert_some hashtable_insert
+ *
+ */
+
+/*****************************************************************************
+ * create_hashtable
+
+ * @name create_hashtable
+ * @param minsize minimum initial size of hashtable
+ * @param hashfunction function for hashing keys
+ * @param key_eq_fn function for determining key equality
+ * @param value_free_fn function to free value (on removal/destroing)
+ * @return newly created hashtable or NULL on failure
+ */
+
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashfunction) (void*),
+ int (*key_eq_fn) (void*,void*),
+ void (*value_free_fn)(void *v));
+
+/*****************************************************************************
+ * hashtable_insert
+
+ * @name hashtable_insert
+ * @param h the hashtable to insert into
+ * @param k the key - hashtable claims ownership and will free on removal
+ * @param v the value - does not claim ownership
+ * @return non-zero for successful insertion
+ *
+ * This function will cause the table to expand if the insertion would take
+ * the ratio of entries to table size over the maximum load factor.
+ *
+ * This function does not check for repeated insertions with a duplicate key.
+ * The value returned when using a duplicate key is undefined -- when
+ * the hashtable changes size, the order of retrieval of duplicate key
+ * entries is reversed.
+ * If in doubt, remove before insert.
+ */
+
+int
+hashtable_insert(struct hashtable *h, void *k, void *v);
+
+#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \
+int fnname (struct hashtable *h, keytype *k, valuetype *v) \
+{ \
+ return hashtable_insert(h,k,v); \
+}
+
+/*****************************************************************************
+ * hashtable_search
+
+ * @name hashtable_search
+ * @param h the hashtable to search
+ * @param k the key to search for - does not claim ownership
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void *
+hashtable_search(struct hashtable *h, void *k);
+
+#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_search(h,k)); \
+}
+
+/*****************************************************************************
+ * hashtable_remove
+
+ * @name hashtable_remove
+ * @param h the hashtable to remove the item from
+ * @param k the key to search for - does not claim ownership
+ * @param free_value whether to free value (thus it would return NULL)
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void * /* returns value */
+hashtable_remove(struct hashtable *h, void *k, int free_value);
+
+#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_remove(h,k)); \
+}
+
+
+/*****************************************************************************
+ * hashtable_count
+
+ * @name hashtable_count
+ * @param h the hashtable
+ * @return the number of items stored in the hashtable
+ */
+unsigned int
+hashtable_count(struct hashtable *h);
+
+
+/*****************************************************************************
+ * hashtable_destroy
+
+ * @name hashtable_destroy
+ * @param h the hashtable
+ * @param free_values whether to call 'value_free_fn' on the remaining values
+ */
+
+void
+hashtable_destroy(struct hashtable *h, int free_values);
+
+
+/* hashtable_enum_fn:
+ * should return 0 when finished and no more enumeration needed
+ */
+typedef int (*hashtable_enum_fn)(void * key, void * value, void * user_data);
+
+void hashtable_enumerate(struct hashtable * h, hashtable_enum_fn enum_fn, void * user_data);
+
+#endif /* __HASHTABLE_CWC22_H__ */
+
+/*
+ * Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h b/plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h
new file mode 100644
index 0000000000..3bc85f8c15
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#ifndef __HASHTABLE_PRIVATE_CWC22_H__
+#define __HASHTABLE_PRIVATE_CWC22_H__
+
+#include "hashtable.h"
+
+/*****************************************************************************/
+struct entry
+{
+ void *k, *v;
+ unsigned int h;
+ struct entry *next;
+};
+
+struct hashtable {
+ unsigned int tablelength;
+ struct entry **table;
+ unsigned int entrycount;
+ unsigned int loadlimit;
+ unsigned int primeindex;
+ unsigned int (*hashfn) (void *k);
+ int (*eqfn) (void *k1, void *k2);
+ void (*value_free_fn)(void *v);
+};
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k);
+
+/*****************************************************************************/
+/* indexFor */
+static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue) {
+ return (hashvalue % tablelength);
+};
+
+/* Only works if tablelength == 2^N */
+/*static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue)
+{
+ return (hashvalue & (tablelength - 1u));
+}
+*/
+
+/*****************************************************************************/
+#define freekey(X) free(X)
+/*define freekey(X) ; */
+
+
+/*****************************************************************************/
+
+#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/
+
+/*
+ * Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/m_chat.h b/plugins/!NotAdopted/VypressChat/contrib/m_chat.h
new file mode 100644
index 0000000000..8caf5d0e8c
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/m_chat.h
@@ -0,0 +1,424 @@
+/*
+Chat module plugin for Miranda IM
+
+Copyright (C) 2003 Jörgen Persson
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#if 0
+
+/*
+ This plugin provides event driven chat rooms for protocols that wish to use it.
+ It is built for IRC, which I also develop and is naturally biased towards IRC,
+ but it should work very well with other protocols too. I will try to explain as
+ careful as possible in this document how to use chat.dll
+
+ -- General guidelines --
+
+ There is one rule a protocol MUST follow to use this:
+
+ 1. Do not touch contacts that has a byte "ChatRoom" set to ANYTHING other than 0! (Could be 1, 2, 3, ...)
+ This is because chat.dll adds contacts to the clist using the protocol name
+ supplied by the protocol. But this will naturally not work well if the
+ protocol also tampers with the contacts. The value of the BYTE indicates which type of
+ window/contact it is (see the GCW_* flags below). There is two exceptions to this rule:
+
+ * You should continue to handle the right click menu items of these
+ contacts as usual, by hooking the menu prebuild hook etc. Chat.dll can not
+ handle this in an efficient manner!
+
+ * You should also handle when the user deletes the contact/room from the
+ contact list, as the protocol will then most likely have to send some message
+ to the server that the user has left the room.
+
+ 2. The chat.dll plugin keeps its own copies of strings passed.
+
+*/
+
+
+// Example of implementing point 1:
+
+// This is a code snippet that is common in protocols:
+
+
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !lstrcmpi(szProto, PROTONAME))
+ {
+// ... do something with the hContact here;
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+
+
+// You should do this instead:
+
+
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !lstrcmpi(szProto, PROTONAME))
+ {
+ if(DBGetContactSettingByte(hContact, PROTONAME, "ChatRoom", 0) == 0)
+ {
+// ... do something with the hContact here;
+ }
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+
+// There is not more to it than that. To recapitulate: do not touch contacts where the
+// BYTE "ChatRoom" is set to 1, apart from the two exceptions mentioned!
+
+// ... now onto how to use this thing!
+
+
+#endif
+
+
+
+//------------------------- SERVICES ------------------------
+/*
+ -- Register with the chat module --
+
+ The first thing that a protocol need to do is register with chat.dll. This is best done
+ after ALL modules has loaded naturally. The registration is needed to make sure that
+ the protocol obey rule 1 mentioned above, but equally important to set protocol specific
+ settings.
+
+ wParam= NULL
+ lParam= (LPARAM)(GCREGISTER *)gcr
+ returns 0 on success or nonzero on failure
+*/
+
+#define GC_BOLD 0x0001 //enable the 'bold' button
+#define GC_ITALICS 0x0002 //enable the 'italics' button
+#define GC_UNDERLINE 0x0004 //enable the 'underline' button
+#define GC_COLOR 0x0008 //enable the 'foreground color' button
+#define GC_BKGCOLOR 0x0010 //enable the 'background color' button
+#define GC_ACKMSG 0x0020 //the protocol must ack. messages sent
+#define GC_TYPNOTIF 0x0040 //enable typing notifications
+#define GC_CHANMGR 0x0080 //enable the 'channel settings' button
+
+typedef struct {
+ int cbSize; //Set to sizeof();
+ DWORD dwFlags; //Use GC_* flags above
+ const char *pszModule; //This MUST be the protocol name as registered with Miranda IM
+ const char *pszModuleDispName; //This is the protocol name as it will be displayed to the user
+ int iMaxText; //Max message length the protocol supports. Will limit the typing area
+ int nColors; //Number of colors in the colorchooser menu for the color buttons. Max = 100
+ COLORREF* pColors; //pointer to the first static COLORREF array. ie: COLORREF crCols[nColors]; pColors = &crCols[0];
+ } GCREGISTER;
+#define MS_GC_REGISTER "GChat/Register"
+
+
+
+/*
+ -- Tell the chat module to create a new window --
+
+ Create a new chat room and set various settings related to it. This is not
+ the same as actually joining the chat room. The chat room will not be visible
+ to the user until the 'set up' phase is completed. See the MS_GC_EVENT for that.
+
+ wParam=0
+ lParam=(LPARAM)(GCWINDOW*)gcw
+
+ returns 0 on success or nonzero on failure
+*/
+#define GCW_CHATROOM 1
+#define GCW_SERVER 2
+#define GCW_PRIVMESS 3
+
+typedef struct {
+ int cbSize; //Set to sizeof();
+ int iType; //Use one of the GCW_* flags above to set the general type of usage for the window
+ const char *pszModule; //The name of the protocol owning the window (the same as pszModule when you register)
+ const char *pszName; //The name of the chat room as it will be displayed to the user
+ const char *pszID; //The unique identifier for the chat room
+ const char *pszStatusbarText; //Optional text to set in the statusbar, or NULL.
+ BOOL bDisableNickList; //Disable the nicklist
+ DWORD dwItemData; //Set user defined data for this chat room. Retrieve it by using the service below
+ } GCWINDOW;
+#define MS_GC_NEWCHAT "GChat/NewChat"
+
+
+
+/*
+ -- Show an event --
+
+ Events is what drives chat.dll! After having created the chat room with
+ MS_GC_NEWCHAT it is time to make it work for real. Start off by telling chat.dll
+ what statuses a user can have by sending GC_EVENT_ADDGROUP as many times as needed.
+ Then send join events
+ (GC_EVENT_JOIN) to populate the user list. You will need to send one event
+ per user that should be added. When you are done with filling the user list
+ it is a good time to end the 'set up' phase and make the window visible by
+ calling a GC_EVENT_VISIBILITY event. A nice tip is to make sure the bAddToLog
+ member of GCEVENT is set to FALSE during the initial filling of the user list.
+
+ The GCDEST structure and its members are always used, but what members of
+ GCEVENT to use is determined by what event is sent to chat.dll. bAddToLog and
+ time is valid (at least where it makes sense). See the description of each event
+ for more information of what members that are valid.
+
+ It is possible to send formatted text (bold, italics, underlined, foreground color
+ and background color) by using the following keywords in pszText:
+ %cRRRGGGBBB - set the foreground color
+ %C - set foreground color to default
+ %fRRRGGGBBB - set the background color
+ %F - set the background color to default
+ %b - enable bold
+ %B - disable bold
+ %u - enable underlined
+ %U - disable underlined
+ %i - enable italics
+ %I - disable italics
+ %r - reset all to default
+ %% - escape the formatting. Translates to %
+
+ wParam=0
+ lParam=(LPARAM)(GCEVENT *) gce
+
+ returns 0 on success or nonzero on failure
+*/
+
+
+// GC_EVENT_JOIN <pszNick> has joined
+// A user is joining the channel, set bIsMe to indicate who is the user
+// pszNick - Display name
+// pszUID - Unique identifier
+// pszStatus - Which group (status) to add the user to
+// bIsMe - Is this the user? Used to indicate that the user has joined the channel
+#define GC_EVENT_JOIN 0x0001
+
+// GC_EVENT_PART <pszNick> has left[: pszText]
+// A user left the chat room
+// pszUID - Unique identifier
+// pszText - part message
+#define GC_EVENT_PART 0x0002
+
+// GC_EVENT_QUIT <pszNick> disconnected[: pszText]
+// A user disconnected, use pszID = NULL (of GCDEST) to broadcast to all windows.
+// pszUID - Unique identifier
+// pszText - part message
+#define GC_EVENT_QUIT 0x0004
+
+// GC_EVENT_KICK <pszStatus> kicked <pszNick>
+// A user is kicking another user from the room
+// pszUID - Unique identifier of the one being kicked
+// pszStatus - Name of user doing the kick
+#define GC_EVENT_KICK 0x0008
+
+// GC_EVENT_NICK <pszNick> is now known as <pszText>
+// A user changed his name
+// NOTE, see GC_EVENT_CHID also
+// pszUID - Unique identifier of the one changing name
+// pszText - New name of the user
+#define GC_EVENT_NICK 0x0010
+
+// GC_EVENT_NOTICE Notice from <pszNick>: <pszText>
+// An IRC type notice, will be sent to the active window
+// pszUID - Unique identifier
+// pszText - Notice text
+#define GC_EVENT_NOTICE 0x0020
+
+// GC_EVENT_MESSAGE
+// A regular chat room message
+// is outgoing or incoming
+// pszUID - Unique identifier
+// pszText - Message text, use the formatting variables above.
+// NOTE make sure % is translated to %% to avoid accidental formatting
+#define GC_EVENT_MESSAGE 0x0040
+
+// GC_EVENT_TOPIC Topic is <pszText>
+// pszUID - Unique identifier
+// pszText - Topic text
+#define GC_EVENT_TOPIC 0x0080
+
+// GC_EVENT_INFORMATION
+// Informational style text
+// pszText - Information text
+#define GC_EVENT_INFORMATION 0x0100
+
+// GC_EVENT_ACTION
+// An IRC Style action event. Same as GC_EVENT_MESSAGE otherwise
+#define GC_EVENT_ACTION 0x0200
+
+// GC_EVENT_ADDSTATUS <pszText> enables '<pszStatus>' for <pszNick>
+// pszUID - Unique identifier
+// pszText - The one enabling the status for another user
+// pszStatus - The status given
+#define GC_EVENT_ADDSTATUS 0x0400
+
+// GC_EVENT_REMOVESTATUS <pszText> disables '<pszStatus>' for <pszNick>
+// pszUID - Unique identifier
+// pszText - The one disabling the status for another user
+// pszStatus - The status taken
+#define GC_EVENT_REMOVESTATUS 0x0800
+
+// GC_EVENT_CHID - not shown in the log
+// Change the unique identifier of a contact
+// pszUID - Unique identifier
+// pszText - The new unique identifier
+#define GC_EVENT_CHID 0x1000
+
+// GC_EVENT_CHID - not shown in the log
+// Change the name of a window
+// pszText - The new name
+#define GC_EVENT_CHWINNAME 0x1001
+
+// GC_EVENT_ADDGROUP - not shown in the log
+// Add a new status group to the user list
+// pszStatus - The new group name
+#define GC_EVENT_ADDGROUP 0x1002
+
+// GC_EVENT_SETITEMDATA GC_EVENT_SETITEMDATA - not shown in the log
+// Get or set the user defined data of a window
+// dwItemData - The itemdata to set or get
+#define GC_EVENT_SETITEMDATA 0x1003
+#define GC_EVENT_GETITEMDATA 0x1004
+
+// GC_EVENT_CONTROL - not shown in the log
+// Call WINDOW_INITDONE after the initial setup is done.
+// Also use it to control aspects of a window if needed .
+// No members of GCEVENT used, send one of the below flags in wParam instead
+#define WINDOW_INITDONE 1 //send when the window is joined and all users have ben added to the nicklist
+#define WINDOW_VISIBLE 2 //make the room visible (most likely you will never use this)
+#define WINDOW_HIDDEN 3 //make the room hidden (most likely you will never use this)
+#define WINDOW_MAXIMIZE 4 //make the room maximized (most likely you will never use this)
+#define WINDOW_MINIMIZE 5 //make the room minimized (most likely you will never use this)
+#define WINDOW_CLEARLOG 6 //clear the log of the room
+#define WINDOW_TERMINATE 7 //send to remove a window from chat.dll,
+#define WINDOW_OFFLINE 8 //send when the user leave the room
+#define WINDOW_ONLINE 9 //send when the user join the room
+
+#define GC_EVENT_CONTROL 0x1005
+
+// GC_EVENT_SETSBTEXT - not shown in the log
+// Set the text of the statusbar
+// pszText - text
+#define GC_EVENT_SETSBTEXT 0x1006
+
+// GC_EVENT_ACK - not shown in the log
+// Used to ack a outgoing message, when GC_ACKMSG is set
+// dwItemData - The itemdata
+#define GC_EVENT_ACK 0x1007
+
+// GC_EVENT_SENDMESSAGE - not shown in the log
+// Send a message from the window as if the user had typed it.
+// Used by IRC to broadcast /AME and /AMSG messages
+// pszText - The text
+#define GC_EVENT_SENDMESSAGE 0x1008
+
+typedef struct {
+ char *pszModule; //Name of the protocol (same as you registered with)
+ char *pszID; //Unique identifier of the room corresponding to the event, or NULL to broadcast to all rooms.
+ int iType; //Use GC_EVENT_* as defined above. Only one event per service call.
+} GCDEST;
+
+typedef struct {
+ int cbSize; // Set to sizeof();
+ GCDEST* pDest; // pointer to a GCDEST structure
+ const char *pszText; // Text, usage depends on type of event (see above), max 2048 characters
+ const char *pszNick; // Nick, usage depends on type of event (see above)
+ const char *pszUID; // Unique identifier, usage depends on type of event (see above)
+ const char *pszStatus; // Status, usage depends on type of event (see above)
+ const char *pszUserInfo; // Additional user information that is displayed in the log only for join, part, quit and nick
+ BOOL bIsMe; // Is this event related to the user?
+ BOOL bAddToLog; // Should this event be added to the message log
+ DWORD dwItemData; // User specified data
+ time_t time; // Time of the event
+ } GCEVENT;
+#define MS_GC_EVENT "GChat/NewEvent"
+
+
+
+
+//------------------------- HOOKS ------------------------
+/*
+ -- user interaction --
+ Hook this to receive notifications about user commands. The below flags will tell what sort of
+ user interaction is taking place and is set in iType of the GCDEST pointer member. The other
+ members of GCDEST will tell what protocol and chat room name it is.
+
+ wParam=0
+ lParam=(LPARAM)(GCEVENT *)pgch
+
+ Returning nonzero from your hook will stop other hooks from being called.
+*/
+#define GC_USER_MESSAGE 1 // user typed a message, with \n delimiting lines, valid members: pszText
+#define GC_USER_CHANMGR 2 // user clicked the chat room settings button
+#define GC_USER_LOGMENU 3 // user has chosen a message log menu item, valid members: dwData
+#define GC_USER_NICKLISTMENU 4 // user has chosen a user list menu item, valid members: dwData
+#define GC_USER_TYPNOTIFY 5 // user is typing
+#define GC_USER_PRIVMESS 6 // user wants to talk privately to user, valid members: pszText, pszUID
+#define GC_USER_TERMINATE 7 // a chat window is about to be closed, useful for freeing the Item data which is passed in dwData, valid members: dwData
+#define ME_GC_EVENT "GChat/OutgoingEvent"
+
+typedef struct {
+ GCDEST* pDest; // Same meaning as for MS_GC_EVENT
+ char * pszText; // Text
+ char * pszUID; // Unique identifier
+ DWORD dwData; // user data
+ } GCHOOK;
+
+
+/*
+ -- Build the pop up menus --
+ The user is activating a right click menu and the protocol should tell what
+ Items should be added to the menu. You should have a static array of struct gc_item's.
+ When the hook is fired the protocol should set nItems to the number of gc_item's
+ it want to add and then set Item to point to that array.
+
+ wParam=0
+ lParam=(LPARAM)(GCMENUITEM *)gcmi
+
+ Returning nonzero from your hook will stop other hooks from being called.
+
+*/
+
+#define MENU_NEWPOPUP 1 // add submenu
+#define MENU_POPUPITEM 2 // add item to current submenu
+#define MENU_POPUPSEPARATOR 3 // add separator to current submenu
+#define MENU_SEPARATOR 4 // add separator to menu
+#define MENU_ITEM 5 // add item
+struct gc_item {
+ char * pszDesc; // Textual description of the menu item to add
+ DWORD dwID; // must not be 0, must be unique. Will be returned via the above hook when the user click the item
+ int uType; // What kind of item is it?
+ BOOL bDisabled; // should the item be disabled
+ };
+
+#define MENU_ON_LOG 1 // pop up menu on the log
+#define MENU_ON_NICKLIST 2 // pop up menu on the user list
+typedef struct {
+ char * pszModule; // Set by chat.dll to the protocol name, do not change.
+ char * pszID; // The unique identifier of the window
+ char * pszUID; // The unique identifier of the user, if clicked in the user list
+ int Type; // MENU_ON_LOG or MENU_ON_USERLIST, what menu type is it?
+ int nItems; // set to number of items
+ struct gc_item* Item; // pointer to the first in the array of gc_item's
+ } GCMENUITEMS;
+#define ME_GC_BUILDMENU "GChat/BuildMenu"
+
diff --git a/plugins/!NotAdopted/VypressChat/contrib/memwatch.c b/plugins/!NotAdopted/VypressChat/contrib/memwatch.c
new file mode 100644
index 0000000000..fc59fa40a1
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/memwatch.c
@@ -0,0 +1,2664 @@
+/*
+** MEMWATCH.C
+** Nonintrusive ANSI C memory leak / overwrite detection
+** Copyright (C) 1992-2003 Johan Lindh
+** All rights reserved.
+** Version 2.71
+
+ This file is part of MEMWATCH.
+
+ MEMWATCH is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ MEMWATCH is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with MEMWATCH; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+**
+** 920810 JLI [1.00]
+** 920830 JLI [1.10 double-free detection]
+** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit]
+** 921022 JLI [1.20 ASSERT and VERIFY]
+** 921105 JLI [1.30 C++ support and TRACE]
+** 921116 JLI [1.40 mwSetOutFunc]
+** 930215 JLI [1.50 modified ASSERT/VERIFY]
+** 930327 JLI [1.51 better auto-init & PC-lint support]
+** 930506 JLI [1.55 MemWatch class, improved C++ support]
+** 930507 JLI [1.60 mwTest & CHECK()]
+** 930809 JLI [1.65 Abort/Retry/Ignore]
+** 930820 JLI [1.70 data dump when unfreed]
+** 931016 JLI [1.72 modified C++ new/delete handling]
+** 931108 JLI [1.77 mwSetAssertAction() & some small changes]
+** 940110 JLI [1.80 no-mans-land alloc/checking]
+** 940328 JLI [2.00 version 2.0 rewrite]
+** Improved NML (no-mans-land) support.
+** Improved performance (especially for free()ing!).
+** Support for 'read-only' buffers (checksums)
+** ^^ NOTE: I never did this... maybe I should?
+** FBI (free'd block info) tagged before freed blocks
+** Exporting of the mwCounter variable
+** mwBreakOut() localizes debugger support
+** Allocation statistics (global, per-module, per-line)
+** Self-repair ability with relinking
+** 950913 JLI [2.10 improved garbage handling]
+** 951201 JLI [2.11 improved auto-free in emergencies]
+** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()]
+** 960514 JLI [2.12 undefining of existing macros]
+** 960515 JLI [2.13 possibility to use default new() & delete()]
+** 960516 JLI [2.20 suppression of file flushing on unfreed msgs]
+** 960516 JLI [2.21 better support for using MEMWATCH with DLL's]
+** 960710 JLI [X.02 multiple logs and mwFlushNow()]
+** 960801 JLI [2.22 merged X.01 version with current]
+** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
+** 960805 JLI [2.31 merged X.02 version with current]
+** 961002 JLI [2.32 support for realloc() + fixed STDERR bug]
+** 961222 JLI [2.40 added mwMark() & mwUnmark()]
+** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY]
+** 970113 JLI [2.42 added support for PC-Lint 7.00g]
+** 970207 JLI [2.43 added support for strdup()]
+** 970209 JLI [2.44 changed default filename to lowercase]
+** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers]
+** 970723 JLI [2.46 added MW_ARI_NULLREAD flag]
+** 970813 JLI [2.47 stabilized marker handling]
+** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway]
+** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support]
+** 980417 JLI [2.51 more checks for invalid addresses]
+** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting]
+** 990112 JLI [2.53 added check for empty heap to mwIsOwned]
+** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML]
+** 990224 JLI [2.56 changed ordering of members in structures]
+** 990303 JLI [2.57 first maybe-fixit-for-hpux test]
+** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit]
+** 990517 JLI [2.59 fixed some high-sensitivity warnings]
+** 990610 JLI [2.60 fixed some more high-sensitivity warnings]
+** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names]
+** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()]
+** 991007 JLI [2.63 first shot at a 64-bit compatible version]
+** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const]
+** 000704 JLI [2.65 added some more detection for 64-bits]
+** 010502 JLI [2.66 incorporated some user fixes]
+** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)]
+** [added array destructor for C++ (thanks rdasilva@connecttel.com)]
+** [added mutex support (thanks rdasilva@connecttel.com)]
+** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined]
+** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked]
+** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen]
+** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)]
+** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)]
+*/
+
+#define __MEMWATCH_C 1
+
+#ifdef MW_NOCPP
+#define MEMWATCH_NOCPP
+#endif
+#ifdef MW_STDIO
+#define MEMWATCH_STDIO
+#endif
+
+/***********************************************************************
+** Include files
+***********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <time.h>
+#include <limits.h>
+#include "memwatch.h"
+
+#ifndef toupper
+#include <ctype.h>
+#endif
+
+#if defined(WIN32) || defined(__WIN32__)
+#define MW_HAVE_MUTEX 1
+#include <windows.h>
+#endif
+
+#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
+#define MW_HAVE_MUTEX 1
+#include <pthread.h>
+#endif
+
+/***********************************************************************
+** Defines & other weird stuff
+***********************************************************************/
+
+/*lint -save -e767 */
+#define VERSION "2.71" /* the current version number */
+#define CHKVAL(mw) (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line)
+#define FLUSH() mwFlush()
+#define TESTS(f,l) if(mwTestAlways) (void)mwTestNow(f,l,1)
+#define PRECHK 0x01234567L
+#define POSTCHK 0x76543210L
+#define mwBUFFER_TO_MW(p) ( (mwData*) (void*) ( ((char*)p)-mwDataSize-mwOverflowZoneSize ) )
+/*lint -restore */
+
+#define MW_NML 0x0001
+
+#ifdef _MSC_VER
+#define COMMIT "c" /* Microsoft C requires the 'c' to perform as desired */
+#else
+#define COMMIT "" /* Normal ANSI */
+#endif /* _MSC_VER */
+
+#ifdef __cplusplus
+#define CPPTEXT "++"
+#else
+#define CPPTEXT ""
+#endif /* __cplusplus */
+
+#ifdef MEMWATCH_STDIO
+#define mwSTDERR stderr
+#else
+#define mwSTDERR mwLog
+#endif
+
+#ifdef MW_HAVE_MUTEX
+#define MW_MUTEX_INIT() mwMutexInit()
+#define MW_MUTEX_TERM() mwMutexTerm()
+#define MW_MUTEX_LOCK() mwMutexLock()
+#define MW_MUTEX_UNLOCK() mwMutexUnlock()
+#else
+#define MW_MUTEX_INIT()
+#define MW_MUTEX_TERM()
+#define MW_MUTEX_LOCK()
+#define MW_MUTEX_UNLOCK()
+#endif
+
+/***********************************************************************
+** If you really, really know what you're doing,
+** you can predefine these things yourself.
+***********************************************************************/
+
+#ifndef mwBYTE_DEFINED
+# if CHAR_BIT != 8
+# error need CHAR_BIT to be 8!
+# else
+typedef unsigned char mwBYTE;
+# define mwBYTE_DEFINED 1
+# endif
+#endif
+
+#if defined(ULONGLONG_MAX) || defined(ULLONG_MAX) || defined(_UI64_MAX) || defined(ULONG_LONG_MAX)
+# define mw64BIT 1
+# define mwROUNDALLOC_DEFAULT 8
+#else
+# if UINT_MAX <= 0xFFFFUL
+# define mw16BIT 1
+# define mwROUNDALLOC_DEFAULT 2
+# else
+# if ULONG_MAX > 0xFFFFFFFFUL
+# define mw64BIT 1
+# define mwROUNDALLOC_DEFAULT 8
+# else
+# define mw32BIT 1
+# define mwROUNDALLOC_DEFAULT 4
+# endif
+# endif
+#endif
+
+/* mwROUNDALLOC is the number of bytes to */
+/* round up to, to ensure that the end of */
+/* the buffer is suitable for storage of */
+/* any kind of object */
+#ifndef mwROUNDALLOC
+# define mwROUNDALLOC mwROUNDALLOC_DEFAULT
+#endif
+
+#ifndef mwDWORD_DEFINED
+#if ULONG_MAX == 0xFFFFFFFFUL
+typedef unsigned long mwDWORD;
+#define mwDWORD_DEFINED "unsigned long"
+#endif
+#endif
+
+#ifndef mwDWORD_DEFINED
+#if UINT_MAX == 0xFFFFFFFFUL
+typedef unsigned int mwDWORD;
+#define mwDWORD_DEFINED "unsigned int"
+#endif
+#endif
+
+#ifndef mwDWORD_DEFINED
+#if USHRT_MAX == 0xFFFFFFFFUL
+typedef unsigned short mwDWORD;
+#define mwDWORD_DEFINED "unsigned short"
+#endif
+#endif
+
+#ifndef mwBYTE_DEFINED
+#error "can't find out the correct type for a 8 bit scalar"
+#endif
+
+#ifndef mwDWORD_DEFINED
+#error "can't find out the correct type for a 32 bit scalar"
+#endif
+
+/***********************************************************************
+** Typedefs & structures
+***********************************************************************/
+
+/* main data holding area, precedes actual allocation */
+typedef struct mwData_ mwData;
+struct mwData_ {
+ mwData* prev; /* previous allocation in chain */
+ mwData* next; /* next allocation in chain */
+ const char* file; /* file name where allocated */
+ long count; /* action count */
+ long check; /* integrity check value */
+#if 0
+ long crc; /* data crc value */
+#endif
+ size_t size; /* size of allocation */
+ int line; /* line number where allocated */
+ unsigned flag; /* flag word */
+ };
+
+/* statistics structure */
+typedef struct mwStat_ mwStat;
+struct mwStat_ {
+ mwStat* next; /* next statistic buffer */
+ const char* file;
+ long total; /* total bytes allocated */
+ long num; /* total number of allocations */
+ long max; /* max allocated at one time */
+ long curr; /* current allocations */
+ int line;
+ };
+
+/* grabbing structure, 1K in size */
+typedef struct mwGrabData_ mwGrabData;
+struct mwGrabData_ {
+ mwGrabData* next;
+ int type;
+ char blob[ 1024 - sizeof(mwGrabData*) - sizeof(int) ];
+ };
+
+typedef struct mwMarker_ mwMarker;
+struct mwMarker_ {
+ void *host;
+ char *text;
+ mwMarker *next;
+ int level;
+ };
+
+#if defined(WIN32) || defined(__WIN32__)
+typedef HANDLE mwMutex;
+#endif
+
+#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
+typedef pthread_mutex_t mwMutex;
+#endif
+
+/***********************************************************************
+** Static variables
+***********************************************************************/
+
+static int mwInited = 0;
+static int mwInfoWritten = 0;
+static int mwUseAtexit = 0;
+static FILE* mwLog = NULL;
+static int mwFlushing = 0;
+static int mwStatLevel = MW_STAT_DEFAULT;
+static int mwNML = MW_NML_DEFAULT;
+static int mwFBI = 0;
+static long mwAllocLimit = 0L;
+static int mwUseLimit = 0;
+
+static long mwNumCurAlloc = 0L;
+static mwData* mwHead = NULL;
+static mwData* mwTail = NULL;
+static int mwDataSize = 0;
+static unsigned char mwOverflowZoneTemplate[] = "mEmwAtch";
+static int mwOverflowZoneSize = mwROUNDALLOC;
+
+static void (*mwOutFunction)(int) = NULL;
+static int (*mwAriFunction)(const char*) = NULL;
+static int mwAriAction = MW_ARI_ABORT;
+
+static char mwPrintBuf[MW_TRACE_BUFFER+8];
+
+static unsigned long mwCounter = 0L;
+static long mwErrors = 0L;
+
+static int mwTestFlags = 0;
+static int mwTestAlways = 0;
+
+static FILE* mwLogB1 = NULL;
+static int mwFlushingB1 = 0;
+
+static mwStat* mwStatList = NULL;
+static long mwStatTotAlloc = 0L;
+static long mwStatMaxAlloc = 0L;
+static long mwStatNumAlloc = 0L;
+static long mwStatCurAlloc = 0L;
+static long mwNmlNumAlloc = 0L;
+static long mwNmlCurAlloc = 0L;
+
+static mwGrabData* mwGrabList = NULL;
+static long mwGrabSize = 0L;
+
+static void * mwLastFree[MW_FREE_LIST];
+static const char *mwLFfile[MW_FREE_LIST];
+static int mwLFline[MW_FREE_LIST];
+static int mwLFcur = 0;
+
+static mwMarker* mwFirstMark = NULL;
+
+static FILE* mwLogB2 = NULL;
+static int mwFlushingB2 = 0;
+
+#ifdef MW_HAVE_MUTEX
+static mwMutex mwGlobalMutex;
+#endif
+
+/***********************************************************************
+** Static function declarations
+***********************************************************************/
+
+static void mwAutoInit( void );
+static FILE* mwLogR( void );
+static void mwLogW( FILE* );
+static int mwFlushR( void );
+static void mwFlushW( int );
+static void mwFlush( void );
+static void mwIncErr( void );
+static void mwUnlink( mwData*, const char* file, int line );
+static int mwRelink( mwData*, const char* file, int line );
+static int mwIsHeapOK( mwData *mw );
+static int mwIsOwned( mwData* mw, const char* file, int line );
+static int mwTestBuf( mwData* mw, const char* file, int line );
+static void mwDefaultOutFunc( int );
+static void mwWrite( const char* format, ... );
+static void mwLogFile( const char* name );
+static size_t mwFreeUp( size_t, int );
+static const void *mwTestMem( const void *, unsigned, int );
+static int mwStrCmpI( const char *s1, const char *s2 );
+static int mwTestNow( const char *file, int line, int always_invoked );
+static void mwDropAll( void );
+static const char *mwGrabType( int type );
+static unsigned mwGrab_( unsigned kb, int type, int silent );
+static unsigned mwDrop_( unsigned kb, int type, int silent );
+static int mwARI( const char* text );
+static void mwStatReport( void );
+static mwStat* mwStatGet( const char*, int, int );
+static void mwStatAlloc( size_t, const char*, int );
+static void mwStatFree( size_t, const char*, int );
+static int mwCheckOF( const void * p );
+static void mwWriteOF( void * p );
+static char mwDummy( char c );
+#ifdef MW_HAVE_MUTEX
+static void mwMutexInit( void );
+static void mwMutexTerm( void );
+static void mwMutexLock( void );
+static void mwMutexUnlock( void );
+#endif
+
+/***********************************************************************
+** System functions
+***********************************************************************/
+
+void mwInit( void ) {
+ time_t tid;
+
+ if( mwInited++ > 0 ) return;
+
+ MW_MUTEX_INIT();
+
+ /* start a log if none is running */
+ if( mwLogR() == NULL ) mwLogFile( "memwatch.log" );
+ if( mwLogR() == NULL ) {
+ int i;
+ char buf[32];
+ /* oops, could not open it! */
+ /* probably because it's already open */
+ /* so we try some other names */
+ for( i=1; i<100; i++ ) {
+ sprintf( buf, "memwat%02d.log", i );
+ mwLogFile( buf );
+ if( mwLogR() != NULL ) break;
+ }
+ }
+
+ /* initialize the statistics */
+ mwStatList = NULL;
+ mwStatTotAlloc = 0L;
+ mwStatCurAlloc = 0L;
+ mwStatMaxAlloc = 0L;
+ mwStatNumAlloc = 0L;
+ mwNmlCurAlloc = 0L;
+ mwNmlNumAlloc = 0L;
+
+ /* calculate the buffer size to use for a mwData */
+ mwDataSize = sizeof(mwData);
+ while( mwDataSize % mwROUNDALLOC ) mwDataSize ++;
+
+ /* write informational header if needed */
+ if( !mwInfoWritten ) {
+ mwInfoWritten = 1;
+ (void) time( &tid );
+ mwWrite(
+ "\n============="
+ " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh "
+ "=============\n");
+ mwWrite( "\nStarted at %s\n", ctime( &tid ) );
+
+/**************************************************************** Generic */
+ mwWrite( "Modes: " );
+#ifdef mwNew
+ mwWrite( "C++ " );
+#endif /* mwNew */
+#ifdef __STDC__
+ mwWrite( "__STDC__ " );
+#endif /* __STDC__ */
+#ifdef mw16BIT
+ mwWrite( "16-bit " );
+#endif
+#ifdef mw32BIT
+ mwWrite( "32-bit " );
+#endif
+#ifdef mw64BIT
+ mwWrite( "64-bit " );
+#endif
+ mwWrite( "mwDWORD==(" mwDWORD_DEFINED ")\n" );
+ mwWrite( "mwROUNDALLOC==%d sizeof(mwData)==%d mwDataSize==%d\n",
+ mwROUNDALLOC, sizeof(mwData), mwDataSize );
+/**************************************************************** Generic */
+
+/************************************************************ Microsoft C */
+#ifdef _MSC_VER
+ mwWrite( "Compiled using Microsoft C" CPPTEXT
+ " %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100 );
+#endif /* _MSC_VER */
+/************************************************************ Microsoft C */
+
+/************************************************************** Borland C */
+#ifdef __BORLANDC__
+ mwWrite( "Compiled using Borland C"
+#ifdef __cplusplus
+ "++ %d.%01d\n", __BCPLUSPLUS__/0x100, (__BCPLUSPLUS__%0x100)/0x10 );
+#else
+ " %d.%01d\n", __BORLANDC__/0x100, (__BORLANDC__%0x100)/0x10 );
+#endif /* __cplusplus */
+#endif /* __BORLANDC__ */
+/************************************************************** Borland C */
+
+/************************************************************** Watcom C */
+#ifdef __WATCOMC__
+ mwWrite( "Compiled using Watcom C %d.%02d ",
+ __WATCOMC__/100, __WATCOMC__%100 );
+#ifdef __FLAT__
+ mwWrite( "(32-bit flat model)" );
+#endif /* __FLAT__ */
+ mwWrite( "\n" );
+#endif /* __WATCOMC__ */
+/************************************************************** Watcom C */
+
+ mwWrite( "\n" );
+ FLUSH();
+ }
+
+ if( mwUseAtexit ) (void) atexit( mwAbort );
+ return;
+ }
+
+void mwAbort( void ) {
+ mwData *mw;
+ mwMarker *mrk;
+ char *data;
+ time_t tid;
+ int c, i, j;
+ int errors;
+
+ tid = time( NULL );
+ mwWrite( "\nStopped at %s\n", ctime( &tid) );
+
+ if( !mwInited )
+ mwWrite( "internal: mwAbort(): MEMWATCH not initialized!\n" );
+
+ /* release the grab list */
+ mwDropAll();
+
+ /* report mwMarked items */
+ while( mwFirstMark ) {
+ mrk = mwFirstMark->next;
+ mwWrite( "mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text );
+ free( mwFirstMark->text );
+ free( mwFirstMark );
+ mwFirstMark = mrk;
+ mwErrors ++;
+ }
+
+ /* release all still allocated memory */
+ errors = 0;
+ while( mwHead != NULL && errors < 3 ) {
+ if( !mwIsOwned(mwHead, __FILE__, __LINE__ ) ) {
+ if( errors < 3 )
+ {
+ errors ++;
+ mwWrite( "internal: NML/unfreed scan restarting\n" );
+ FLUSH();
+ mwHead = mwHead;
+ continue;
+ }
+ mwWrite( "internal: NML/unfreed scan aborted, heap too damaged\n" );
+ FLUSH();
+ break;
+ }
+ mwFlushW(0);
+ if( !(mwHead->flag & MW_NML) ) {
+ mwErrors++;
+ data = ((char*)mwHead)+mwDataSize;
+ mwWrite( "unfreed: <%ld> %s(%d), %ld bytes at %p ",
+ mwHead->count, mwHead->file, mwHead->line, (long)mwHead->size, data+mwOverflowZoneSize );
+ if( mwCheckOF( data ) ) {
+ mwWrite( "[underflowed] ");
+ FLUSH();
+ }
+ if( mwCheckOF( (data+mwOverflowZoneSize+mwHead->size) ) ) {
+ mwWrite( "[overflowed] ");
+ FLUSH();
+ }
+ mwWrite( " \t{" );
+ j = 16; if( mwHead->size < 16 ) j = (int) mwHead->size;
+ for( i=0;i<16;i++ ) {
+ if( i<j ) mwWrite( "%02X ",
+ (unsigned char) *(data+mwOverflowZoneSize+i) );
+ else mwWrite( ".. " );
+ }
+ for( i=0;i<j;i++ ) {
+ c = *(data+mwOverflowZoneSize+i);
+ if( c < 32 || c > 126 ) c = '.';
+ mwWrite( "%c", c );
+ }
+ mwWrite( "}\n" );
+ mw = mwHead;
+ mwUnlink( mw, __FILE__, __LINE__ );
+ free( mw );
+ }
+ else {
+ data = ((char*)mwHead) + mwDataSize + mwOverflowZoneSize;
+ if( mwTestMem( data, mwHead->size, MW_VAL_NML ) ) {
+ mwErrors++;
+ mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
+ mwHead->count, data + mwOverflowZoneSize, mwHead->file, mwHead->line );
+ FLUSH();
+ }
+ mwNmlNumAlloc --;
+ mwNmlCurAlloc -= mwHead->size;
+ mw = mwHead;
+ mwUnlink( mw, __FILE__, __LINE__ );
+ free( mw );
+ }
+ }
+
+ if( mwNmlNumAlloc ) mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc );
+ if( mwNmlCurAlloc ) mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc );
+
+ /* report statistics */
+ mwStatReport();
+ FLUSH();
+
+ mwInited = 0;
+ mwHead = mwTail = NULL;
+ if( mwErrors )
+ fprintf(mwSTDERR,"MEMWATCH detected %ld anomalies\n",mwErrors);
+ mwLogFile( NULL );
+ mwErrors = 0;
+
+ MW_MUTEX_TERM();
+
+ }
+
+void mwTerm( void ) {
+ if( mwInited == 1 )
+ {
+ mwAbort();
+ return;
+ }
+ if( !mwInited )
+ mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n");
+ else
+ mwInited --;
+ }
+
+void mwStatistics( int level )
+{
+ mwAutoInit();
+ if( level<0 ) level=0;
+ if( mwStatLevel != level )
+ {
+ mwWrite( "statistics: now collecting on a %s basis\n",
+ level<1?"global":(level<2?"module":"line") );
+ mwStatLevel = level;
+ }
+}
+
+void mwAutoCheck( int onoff ) {
+ mwAutoInit();
+ mwTestAlways = onoff;
+ if( onoff ) mwTestFlags = MW_TEST_ALL;
+ }
+
+void mwSetOutFunc( void (*func)(int) ) {
+ mwAutoInit();
+ mwOutFunction = func;
+ }
+
+static void mwWriteOF( void *p )
+{
+ int i;
+ unsigned char *ptr;
+ ptr = (unsigned char*) p;
+ for( i=0; i<mwOverflowZoneSize; i++ )
+ {
+ *(ptr+i) = mwOverflowZoneTemplate[i%8];
+ }
+ return;
+}
+
+static int mwCheckOF( const void *p )
+{
+ int i;
+ const unsigned char *ptr;
+ ptr = (const unsigned char *) p;
+ for( i=0; i<mwOverflowZoneSize; i++ )
+ {
+ if( *(ptr+i) != mwOverflowZoneTemplate[i%8] )
+ return 1; /* errors found */
+ }
+ return 0; /* no errors */
+}
+
+int mwTest( const char *file, int line, int items ) {
+ mwAutoInit();
+ mwTestFlags = items;
+ return mwTestNow( file, line, 0 );
+ }
+
+/*
+** Returns zero if there are no errors.
+** Returns nonzero if there are errors.
+*/
+int mwTestBuffer( const char *file, int line, void *p ) {
+ mwData* mw;
+
+ mwAutoInit();
+
+ /* do the quick ownership test */
+ mw = (mwData*) mwBUFFER_TO_MW( p );
+
+ if( mwIsOwned( mw, file, line ) ) {
+ return mwTestBuf( mw, file, line );
+ }
+ return 1;
+ }
+
+void mwBreakOut( const char* cause ) {
+ fprintf(mwSTDERR, "breakout: %s\n", cause);
+ mwWrite("breakout: %s\n", cause );
+ return;
+ }
+
+/*
+** 981217 JLI: is it possible that ->next is not always set?
+*/
+void * mwMark( void *p, const char *desc, const char *file, unsigned line ) {
+ mwMarker *mrk;
+ unsigned n, isnew;
+ char *buf;
+ int tot, oflow = 0;
+ char wherebuf[128];
+
+ mwAutoInit();
+ TESTS(NULL,0);
+
+ if( desc == NULL ) desc = "unknown";
+ if( file == NULL ) file = "unknown";
+
+ tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line );
+ if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; }
+
+ if( p == NULL ) {
+ mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc );
+ return p;
+ }
+
+ if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) )
+ {
+ mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n",
+ file, line, mwFirstMark, desc );
+ return p;
+ }
+
+ for( mrk=mwFirstMark; mrk; mrk=mrk->next )
+ {
+ if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) )
+ {
+ mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n",
+ file, line, mrk, mrk->next, desc );
+ return p;
+ }
+ if( mrk->host == p ) break;
+ }
+
+ if( mrk == NULL ) {
+ isnew = 1;
+ mrk = (mwMarker*) malloc( sizeof( mwMarker ) );
+ if( mrk == NULL ) {
+ mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
+ return p;
+ }
+ mrk->next = NULL;
+ n = 0;
+ }
+ else {
+ isnew = 0;
+ n = strlen( mrk->text );
+ }
+
+ n += strlen( wherebuf );
+ buf = (char*) malloc( n+3 );
+ if( buf == NULL ) {
+ if( isnew ) free( mrk );
+ mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
+ return p;
+ }
+
+ if( isnew ) {
+ memcpy( buf, wherebuf, n+1 );
+ mrk->next = mwFirstMark;
+ mrk->host = p;
+ mrk->text = buf;
+ mrk->level = 1;
+ mwFirstMark = mrk;
+ }
+ else {
+ strcpy( buf, mrk->text );
+ strcat( buf, ", " );
+ strcat( buf, wherebuf );
+ free( mrk->text );
+ mrk->text = buf;
+ mrk->level ++;
+ }
+
+ if( oflow ) {
+ mwIncErr();
+ mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
+ }
+ return p;
+ }
+
+void* mwUnmark( void *p, const char *file, unsigned line ) {
+ mwMarker *mrk, *prv;
+ mrk = mwFirstMark;
+ prv = NULL;
+ while( mrk ) {
+ if( mrk->host == p ) {
+ if( mrk->level < 2 ) {
+ if( prv ) prv->next = mrk->next;
+ else mwFirstMark = mrk->next;
+ free( mrk->text );
+ free( mrk );
+ return p;
+ }
+ mrk->level --;
+ return p;
+ }
+ prv = mrk;
+ mrk = mrk->next;
+ }
+ mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p );
+ return p;
+ }
+
+
+/***********************************************************************
+** Abort/Retry/Ignore handlers
+***********************************************************************/
+
+static int mwARI( const char *estr ) {
+ char inbuf[81];
+ int c;
+ fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr);
+ (void) fgets(inbuf,sizeof(inbuf),stdin);
+ for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ;
+ c = inbuf[c];
+ if( c == 'R' || c == 'r' ) {
+ mwBreakOut( estr );
+ return MW_ARI_RETRY;
+ }
+ if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE;
+ return MW_ARI_ABORT;
+ }
+
+/* standard ARI handler (exported) */
+int mwAriHandler( const char *estr ) {
+ mwAutoInit();
+ return mwARI( estr );
+ }
+
+/* used to set the ARI function */
+void mwSetAriFunc( int (*func)(const char *) ) {
+ mwAutoInit();
+ mwAriFunction = func;
+ }
+
+/***********************************************************************
+** Allocation handlers
+***********************************************************************/
+
+void* mwMalloc( size_t size, const char* file, int line) {
+ size_t needed;
+ mwData *mw;
+ char *ptr;
+ void *p;
+
+ mwAutoInit();
+
+ MW_MUTEX_LOCK();
+
+ TESTS(file,line);
+
+ mwCounter ++;
+ needed = mwDataSize + mwOverflowZoneSize*2 + size;
+ if( needed < size )
+ {
+ /* theoretical case: req size + mw overhead exceeded size_t limits */
+ return NULL;
+ }
+
+ /* if this allocation would violate the limit, fail it */
+ if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) {
+ mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
+ mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc );
+ mwIncErr();
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+ mw = (mwData*) malloc( needed );
+ if( mw == NULL ) {
+ if( mwFreeUp(needed,0) >= needed ) {
+ mw = (mwData*) malloc(needed);
+ if( mw == NULL ) {
+ mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed );
+ mwIncErr();
+ FLUSH();
+ }
+ }
+ if( mw == NULL ) {
+ mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n",
+ mwCounter, file, line, (long)size, mwStatCurAlloc );
+ mwIncErr();
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+ }
+
+ mw->count = mwCounter;
+ mw->prev = NULL;
+ mw->next = mwHead;
+ mw->file = file;
+ mw->size = size;
+ mw->line = line;
+ mw->flag = 0;
+ mw->check = CHKVAL(mw);
+
+ if( mwHead ) mwHead->prev = mw;
+ mwHead = mw;
+ if( mwTail == NULL ) mwTail = mw;
+
+ ptr = ((char*)mw) + mwDataSize;
+ mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */
+ ptr += mwOverflowZoneSize;
+ p = ptr;
+ memset( ptr, MW_VAL_NEW, size );
+ ptr += size;
+ mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */
+
+ mwNumCurAlloc ++;
+ mwStatCurAlloc += (long) size;
+ mwStatTotAlloc += (long) size;
+ if( mwStatCurAlloc > mwStatMaxAlloc )
+ mwStatMaxAlloc = mwStatCurAlloc;
+ mwStatNumAlloc ++;
+
+ if( mwStatLevel ) mwStatAlloc( size, file, line );
+
+ MW_MUTEX_UNLOCK();
+ return p;
+ }
+
+void* mwRealloc( void *p, size_t size, const char* file, int line) {
+ int oldUseLimit, i;
+ mwData *mw;
+ char *ptr;
+
+ mwAutoInit();
+
+ if( p == NULL ) return mwMalloc( size, file, line );
+ if( size == 0 ) { mwFree( p, file, line ); return NULL; }
+
+ MW_MUTEX_LOCK();
+
+ /* do the quick ownership test */
+ mw = (mwData*) mwBUFFER_TO_MW( p );
+ if( mwIsOwned( mw, file, line ) ) {
+
+ /* if the buffer is an NML, treat this as a double-free */
+ if( mw->flag & MW_NML )
+ {
+ mwIncErr();
+ if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
+ {
+ mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
+ mwCounter, file, line, mw );
+ }
+ goto check_dbl_free;
+ }
+
+ /* if this allocation would violate the limit, fail it */
+ if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) {
+ TESTS(file,line);
+ mwCounter ++;
+ mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
+ mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc );
+ mwIncErr();
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+ /* fake realloc operation */
+ oldUseLimit = mwUseLimit;
+ mwUseLimit = 0;
+ ptr = (char*) mwMalloc( size, file, line );
+ if( ptr != NULL ) {
+ if( size < mw->size )
+ memcpy( ptr, p, size );
+ else
+ memcpy( ptr, p, mw->size );
+ mwFree( p, file, line );
+ }
+ mwUseLimit = oldUseLimit;
+ MW_MUTEX_UNLOCK();
+ return (void*) ptr;
+ }
+
+ /* Unknown pointer! */
+
+ /* using free'd pointer? */
+check_dbl_free:
+ for(i=0;i<MW_FREE_LIST;i++) {
+ if( mwLastFree[i] == p ) {
+ mwIncErr();
+ mwWrite( "realloc: <%ld> %s(%d), %p was"
+ " freed from %s(%d)\n",
+ mwCounter, file, line, p,
+ mwLFfile[i], mwLFline[i] );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+ }
+
+ /* some weird pointer */
+ mwIncErr();
+ mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n",
+ mwCounter, file, line, p );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+char *mwStrdup( const char* str, const char* file, int line ) {
+ size_t len;
+ char *newstring;
+
+ MW_MUTEX_LOCK();
+
+ if( str == NULL ) {
+ mwIncErr();
+ mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n",
+ mwCounter, file, line );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+ len = strlen( str ) + 1;
+ newstring = (char*) mwMalloc( len, file, line );
+ if( newstring != NULL ) memcpy( newstring, str, len );
+ MW_MUTEX_UNLOCK();
+ return newstring;
+ }
+
+void mwFree( void* p, const char* file, int line ) {
+ int i;
+ mwData* mw;
+ char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ];
+
+ /* this code is in support of C++ delete */
+ if( file == NULL ) {
+ mwFree_( p );
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+ mwAutoInit();
+
+ MW_MUTEX_LOCK();
+ TESTS(file,line);
+ mwCounter ++;
+
+ /* on NULL free, write a warning and return */
+ if( p == NULL ) {
+ mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n",
+ mwCounter, file, line );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+ /* do the quick ownership test */
+ mw = (mwData*) mwBUFFER_TO_MW( p );
+
+ if( mwIsOwned( mw, file, line ) ) {
+ (void) mwTestBuf( mw, file, line );
+
+ /* if the buffer is an NML, treat this as a double-free */
+ if( mw->flag & MW_NML )
+ {
+ if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
+ {
+ mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
+ mwCounter, file, line, mw );
+ }
+ goto check_dbl_free;
+ }
+
+ /* update the statistics */
+ mwNumCurAlloc --;
+ mwStatCurAlloc -= (long) mw->size;
+ if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line );
+
+ /* we should either free the allocation or keep it as NML */
+ if( mwNML ) {
+ mw->flag |= MW_NML;
+ mwNmlNumAlloc ++;
+ mwNmlCurAlloc += (long) mw->size;
+ memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size );
+ }
+ else {
+ /* unlink the allocation, and enter the post-free data */
+ mwUnlink( mw, file, line );
+ memset( mw, MW_VAL_DEL,
+ mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize );
+ if( mwFBI ) {
+ memset( mw, '.', mwDataSize + mwOverflowZoneSize );
+ sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line );
+ strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize );
+ }
+ free( mw );
+ }
+
+ /* add the pointer to the last-free track */
+ mwLFfile[ mwLFcur ] = file;
+ mwLFline[ mwLFcur ] = line;
+ mwLastFree[ mwLFcur++ ] = p;
+ if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0;
+
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+ /* check for double-freeing */
+check_dbl_free:
+ for(i=0;i<MW_FREE_LIST;i++) {
+ if( mwLastFree[i] == p ) {
+ mwIncErr();
+ mwWrite( "double-free: <%ld> %s(%d), %p was"
+ " freed from %s(%d)\n",
+ mwCounter, file, line, p,
+ mwLFfile[i], mwLFline[i] );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+ }
+
+ /* some weird pointer... block the free */
+ mwIncErr();
+ mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n",
+ mwCounter, file, line, p );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+void* mwCalloc( size_t a, size_t b, const char *file, int line ) {
+ void *p;
+ size_t size = a * b;
+ p = mwMalloc( size, file, line );
+ if( p == NULL ) return NULL;
+ memset( p, 0, size );
+ return p;
+ }
+
+void mwFree_( void *p ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ free(p);
+ }
+
+void* mwMalloc_( size_t size ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ return malloc( size );
+ }
+
+void* mwRealloc_( void *p, size_t size ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ return realloc( p, size );
+ }
+
+void* mwCalloc_( size_t a, size_t b ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ return calloc( a, b );
+ }
+
+void mwFlushNow( void ) {
+ if( mwLogR() ) fflush( mwLogR() );
+ return;
+ }
+
+void mwDoFlush( int onoff ) {
+ mwFlushW( onoff<1?0:onoff );
+ if( onoff ) if( mwLogR() ) fflush( mwLogR() );
+ return;
+ }
+
+void mwLimit( long lim ) {
+ TESTS(NULL,0);
+ mwWrite("limit: old limit = ");
+ if( !mwAllocLimit ) mwWrite( "none" );
+ else mwWrite( "%ld bytes", mwAllocLimit );
+ mwWrite( ", new limit = ");
+ if( !lim ) {
+ mwWrite( "none\n" );
+ mwUseLimit = 0;
+ }
+ else {
+ mwWrite( "%ld bytes\n", lim );
+ mwUseLimit = 1;
+ }
+ mwAllocLimit = lim;
+ FLUSH();
+ }
+
+void mwSetAriAction( int action ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ mwAriAction = action;
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+int mwAssert( int exp, const char *exps, const char *fn, int ln ) {
+ int i;
+ char buffer[MW_TRACE_BUFFER+8];
+ if( exp ) {
+ return 0;
+ }
+ mwAutoInit();
+ MW_MUTEX_LOCK();
+ TESTS(fn,ln);
+ mwIncErr();
+ mwCounter++;
+ mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
+ if( mwAriFunction != NULL ) {
+ sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps );
+ i = (*mwAriFunction)(buffer);
+ switch( i ) {
+ case MW_ARI_IGNORE:
+ mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ case MW_ARI_RETRY:
+ mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 1;
+ }
+ }
+ else {
+ if( mwAriAction & MW_ARI_IGNORE ) {
+ mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ }
+ fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps );
+ }
+
+ FLUSH();
+ (void) mwTestNow( fn, ln, 1 );
+ FLUSH();
+
+ if( mwAriAction & MW_ARI_NULLREAD ) {
+ /* This is made in an attempt to kick in */
+ /* any debuggers or OS stack traces */
+ FLUSH();
+ /*lint -save -e413 */
+ i = *((int*)NULL);
+ mwDummy( (char)i );
+ /*lint -restore */
+ }
+
+ MW_MUTEX_UNLOCK();
+ exit(255);
+ /* NOT REACHED - the return statement is in to keep */
+ /* stupid compilers from squeaking about differing return modes. */
+ /* Smart compilers instead say 'code unreachable...' */
+ /*lint -save -e527 */
+ return 0;
+ /*lint -restore */
+ }
+
+int mwVerify( int exp, const char *exps, const char *fn, int ln ) {
+ int i;
+ char buffer[MW_TRACE_BUFFER+8];
+ if( exp ) {
+ return 0;
+ }
+ mwAutoInit();
+ MW_MUTEX_LOCK();
+ TESTS(fn,ln);
+ mwIncErr();
+ mwCounter++;
+ mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
+ if( mwAriFunction != NULL ) {
+ sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps );
+ i = (*mwAriFunction)(buffer);
+ if( i == 0 ) {
+ mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ }
+ if( i == 1 ) {
+ mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 1;
+ }
+ }
+ else {
+ if( mwAriAction & MW_ARI_NULLREAD ) {
+ /* This is made in an attempt to kick in */
+ /* any debuggers or OS stack traces */
+ FLUSH();
+ /*lint -save -e413 */
+ i = *((int*)NULL);
+ mwDummy( (char)i );
+ /*lint -restore */
+ }
+ if( mwAriAction & MW_ARI_IGNORE ) {
+ mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ }
+ fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps );
+ }
+ FLUSH();
+ (void) mwTestNow( fn, ln, 1 );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ exit(255);
+ /* NOT REACHED - the return statement is in to keep */
+ /* stupid compilers from squeaking about differing return modes. */
+ /* Smart compilers instead say 'code unreachable...' */
+ /*lint -save -e527 */
+ return 0;
+ /*lint -restore */
+ }
+
+void mwTrace( const char *format, ... ) {
+ int tot, oflow = 0;
+ va_list mark;
+
+ mwAutoInit();
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
+
+ va_start( mark, format );
+ tot = vsprintf( mwPrintBuf, format, mark );
+ va_end( mark );
+ if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
+ for(tot=0;mwPrintBuf[tot];tot++)
+ (*mwOutFunction)( mwPrintBuf[tot] );
+ if( oflow ) {
+ mwIncErr();
+ mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
+ }
+
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ }
+
+
+/***********************************************************************
+** Grab & Drop
+***********************************************************************/
+
+unsigned mwGrab( unsigned kb ) {
+ TESTS(NULL,0);
+ return mwGrab_( kb, MW_VAL_GRB, 0 );
+ }
+
+unsigned mwDrop( unsigned kb ) {
+ TESTS(NULL,0);
+ return mwDrop_( kb, MW_VAL_GRB, 0 );
+ }
+
+static void mwDropAll() {
+ TESTS(NULL,0);
+ (void) mwDrop_( 0, MW_VAL_GRB, 0 );
+ (void) mwDrop_( 0, MW_VAL_NML, 0 );
+ if( mwGrabList != NULL )
+ mwWrite( "internal: the grab list is not empty after mwDropAll()\n");
+ }
+
+static const char *mwGrabType( int type ) {
+ switch( type ) {
+ case MW_VAL_GRB:
+ return "grabbed";
+ case MW_VAL_NML:
+ return "no-mans-land";
+ default:
+ /* do nothing */
+ ;
+ }
+ return "<unknown type>";
+ }
+
+static unsigned mwGrab_( unsigned kb, int type, int silent ) {
+ unsigned i = kb;
+ mwGrabData *gd;
+ if( !kb ) i = kb = 65000U;
+
+ for(;kb;kb--) {
+ if( mwUseLimit &&
+ (mwStatCurAlloc + mwGrabSize + (long)sizeof(mwGrabData) > mwAllocLimit) ) {
+ if( !silent ) {
+ mwWrite("grabbed: all allowed memory to %s (%u kb)\n",
+ mwGrabType(type), i-kb);
+ FLUSH();
+ }
+ return i-kb;
+ }
+ gd = (mwGrabData*) malloc( sizeof(mwGrabData) );
+ if( gd == NULL ) {
+ if( !silent ) {
+ mwWrite("grabbed: all available memory to %s (%u kb)\n",
+ mwGrabType(type), i-kb);
+ FLUSH();
+ }
+ return i-kb;
+ }
+ mwGrabSize += (long) sizeof(mwGrabData);
+ gd->next = mwGrabList;
+ memset( gd->blob, type, sizeof(gd->blob) );
+ gd->type = type;
+ mwGrabList = gd;
+ }
+ if( !silent ) {
+ mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type) );
+ FLUSH();
+ }
+ return i;
+ }
+
+static unsigned mwDrop_( unsigned kb, int type, int silent ) {
+ unsigned i = kb;
+ mwGrabData *gd,*tmp,*pr;
+ const void *p;
+
+ if( mwGrabList == NULL && kb == 0 ) return 0;
+ if( !kb ) i = kb = 60000U;
+
+ pr = NULL;
+ gd = mwGrabList;
+ for(;kb;) {
+ if( gd == NULL ) {
+ if( i-kb > 0 && !silent ) {
+ mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i-kb);
+ FLUSH();
+ }
+ return i-kb;
+ }
+ if( gd->type == type ) {
+ if( pr ) pr->next = gd->next;
+ kb --;
+ tmp = gd;
+ if( mwGrabList == gd ) mwGrabList = gd->next;
+ gd = gd->next;
+ p = mwTestMem( tmp->blob, sizeof( tmp->blob ), type );
+ if( p != NULL ) {
+ mwWrite( "wild pointer: <%ld> %s memory hit at %p\n",
+ mwCounter, mwGrabType(type), p );
+ FLUSH();
+ }
+ mwGrabSize -= (long) sizeof(mwGrabData);
+ free( tmp );
+ }
+ else {
+ pr = gd;
+ gd = gd->next;
+ }
+ }
+ if( !silent ) {
+ mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type) );
+ FLUSH();
+ }
+ return i;
+ }
+
+/***********************************************************************
+** No-Mans-Land
+***********************************************************************/
+
+void mwNoMansLand( int level ) {
+ mwAutoInit();
+ TESTS(NULL,0);
+ switch( level ) {
+ case MW_NML_NONE:
+ (void) mwDrop_( 0, MW_VAL_NML, 0 );
+ break;
+ case MW_NML_FREE:
+ break;
+ case MW_NML_ALL:
+ (void) mwGrab_( 0, MW_VAL_NML, 0 );
+ break;
+ default:
+ return;
+ }
+ mwNML = level;
+ }
+
+/***********************************************************************
+** Static functions
+***********************************************************************/
+
+static void mwAutoInit( void )
+{
+ if( mwInited ) return;
+ mwUseAtexit = 1;
+ mwInit();
+ return;
+}
+
+static FILE *mwLogR() {
+ if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) return mwLog;
+ if( mwLog == mwLogB1 ) mwLogB2 = mwLog;
+ if( mwLog == mwLogB2 ) mwLogB1 = mwLog;
+ if( mwLogB1 == mwLogB2 ) mwLog = mwLogB1;
+ if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) {
+ mwWrite("internal: log file handle damaged and recovered\n");
+ FLUSH();
+ return mwLog;
+ }
+ fprintf(mwSTDERR,"\nMEMWATCH: log file handle destroyed, using mwSTDERR\n" );
+ mwLog = mwLogB1 = mwLogB2 = mwSTDERR;
+ return mwSTDERR;
+ }
+
+static void mwLogW( FILE *p ) {
+ mwLog = mwLogB1 = mwLogB2 = p;
+ }
+
+static int mwFlushR() {
+ if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) return mwFlushing;
+ if( mwFlushing == mwFlushingB1 ) mwFlushingB2 = mwFlushing;
+ if( mwFlushing == mwFlushingB2 ) mwFlushingB1 = mwFlushing;
+ if( mwFlushingB1 == mwFlushingB2 ) mwFlushing = mwFlushingB1;
+ if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) {
+ mwWrite("internal: flushing flag damaged and recovered\n");
+ FLUSH();
+ return mwFlushing;
+ }
+ mwWrite("internal: flushing flag destroyed, so set to true\n");
+ mwFlushing = mwFlushingB1 = mwFlushingB2 = 1;
+ return 1;
+ }
+
+static void mwFlushW( int n ) {
+ mwFlushing = mwFlushingB1 = mwFlushingB2 = n;
+ }
+
+static void mwIncErr() {
+ mwErrors++;
+ mwFlushW( mwFlushR()+1 );
+ FLUSH();
+ }
+
+static void mwFlush() {
+ if( mwLogR() == NULL ) return;
+#ifdef MW_FLUSH
+ fflush( mwLogR() );
+#else
+ if( mwFlushR() ) fflush( mwLogR() );
+#endif
+ return;
+ }
+
+static void mwUnlink( mwData* mw, const char* file, int line ) {
+ if( mw->prev == NULL ) {
+ if( mwHead != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n",
+ mwCounter, file, line, mw );
+ mwHead = mw->next;
+ }
+ else {
+ if( mw->prev->next != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 failure\n",
+ mwCounter, file, line, mw );
+ else mw->prev->next = mw->next;
+ }
+ if( mw->next == NULL ) {
+ if( mwTail != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n",
+ mwCounter, file, line, mw );
+ mwTail = mw->prev;
+ }
+ else {
+ if( mw->next->prev != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 failure\n",
+ mwCounter, file, line, mw );
+ else mw->next->prev = mw->prev;
+ }
+ }
+
+/*
+** Relinking tries to repair a damaged mw block.
+** Returns nonzero if it thinks it successfully
+** repaired the heap chain.
+*/
+static int mwRelink( mwData* mw, const char* file, int line ) {
+ int fails;
+ mwData *mw1, *mw2;
+ long count, size;
+ mwStat *ms;
+
+ if( file == NULL ) file = "unknown";
+
+ if( mw == NULL ) {
+ mwWrite("relink: cannot repair MW at NULL\n");
+ FLUSH();
+ goto emergency;
+ }
+
+ if( !mwIsSafeAddr(mw, mwDataSize) ) {
+ mwWrite("relink: MW-%p is a garbage pointer\n", mw);
+ FLUSH();
+ goto emergency;
+ }
+
+ mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw );
+ FLUSH();
+ fails = 0;
+
+ /* Repair from head */
+ if( mwHead != mw ) {
+ if( !mwIsSafeAddr( mwHead, mwDataSize ) ) {
+ mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw );
+ FLUSH();
+ goto emergency;
+ }
+ for( mw1=mwHead; mw1; mw1=mw1->next ) {
+ if( mw1->next == mw ) {
+ mw->prev = mw1;
+ break;
+ }
+ if( mw1->next &&
+ ( !mwIsSafeAddr(mw1->next, mwDataSize ) || mw1->next->prev != mw1) ) {
+ mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next );
+ FLUSH();
+ goto emergency;
+ }
+ }
+ if( mw1 == NULL ) {
+ mwWrite("relink: MW-%p not found in forward chain search\n", mw );
+ FLUSH();
+ fails ++;
+ }
+ }
+ else
+ {
+ mwWrite( "relink: MW-%p is the head (first) allocation\n", mw );
+ if( mw->prev != NULL )
+ {
+ mwWrite( "relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw );
+ mw->prev = NULL;
+ }
+ }
+
+ /* Repair from tail */
+ if( mwTail != mw ) {
+ if( !mwIsSafeAddr( mwTail, mwDataSize ) ) {
+ mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw );
+ FLUSH();
+ goto emergency;
+ }
+ for( mw1=mwTail; mw1; mw1=mw1->prev ) {
+ if( mw1->prev == mw ) {
+ mw->next = mw1;
+ break;
+ }
+ if( mw1->prev && (!mwIsSafeAddr(mw1->prev, mwDataSize ) || mw1->prev->next != mw1) ) {
+ mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev );
+ FLUSH();
+ goto emergency;
+ }
+ }
+ if( mw1 == NULL ) {
+ mwWrite("relink: MW-%p not found in reverse chain search\n", mw );
+ FLUSH();
+ fails ++;
+ }
+ }
+ else
+ {
+ mwWrite( "relink: MW-%p is the tail (last) allocation\n", mw );
+ if( mw->next != NULL )
+ {
+ mwWrite( "relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw );
+ mw->next = NULL;
+ }
+ }
+
+ if( fails > 1 ) {
+ mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw );
+ FLUSH();
+ goto verifyok;
+ }
+
+ /* restore MW info where possible */
+ if( mwIsReadAddr( mw->file, 1 ) ) {
+ ms = mwStatGet( mw->file, -1, 0 );
+ if( ms == NULL ) mw->file = "<relinked>";
+ }
+ mw->check = CHKVAL(mw);
+ goto verifyok;
+
+ /* Emergency repair */
+ emergency:
+
+ if( mwHead == NULL && mwTail == NULL )
+ {
+ if( mwStatCurAlloc == 0 )
+ mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line );
+ else
+ mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line );
+ FLUSH();
+ return 0;
+ }
+
+ mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line );
+ FLUSH();
+
+ if( mwHead == NULL || mwTail == NULL )
+ {
+ if( mwHead == NULL ) mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail );
+ else mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead );
+ }
+
+ mw1=NULL;
+ if( mwHead != NULL )
+ {
+ if( !mwIsReadAddr( mwHead, mwDataSize ) || mwHead->check != CHKVAL(mwHead) )
+ {
+ mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead );
+ mwHead = NULL;
+ goto scan_reverse;
+ }
+ if( mwHead->prev != NULL )
+ {
+ mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev );
+ }
+ for( mw1=mwHead; mw1; mw1=mw1->next )
+ {
+ if( mw1->next )
+ {
+ if( !mwIsReadAddr(mw1->next,mwDataSize) ||
+ !mw1->next->check != CHKVAL(mw1) ||
+ mw1->next->prev != mw1 )
+ {
+ mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw1, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw1->file, mw1->line );
+ if( mwIsReadAddr(mw1->next,mwDataSize ) )
+ {
+ mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw1->next, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"",
+ mwIsReadAddr(mw1->file,16)?mw1->file:"<garbage-pointer>", mw1->line );
+ }
+ else
+ {
+ mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n",
+ mw1->next );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+
+scan_reverse:
+ mw2=NULL;
+ if( mwTail != NULL )
+ {
+ if( !mwIsReadAddr(mwTail,mwDataSize) || mwTail->check != CHKVAL(mwTail) )
+ {
+ mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail );
+ mwTail = NULL;
+ goto analyze;
+ }
+ if( mwTail->next != NULL )
+ {
+ mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next );
+ }
+ for( mw2=mwTail; mw2; mw2=mw2->prev )
+ {
+ if( mw2->prev )
+ {
+ if( !mwIsReadAddr(mw2->prev,mwDataSize) ||
+ !mw2->prev->check != CHKVAL(mw2) ||
+ mw2->prev->next != mw2 )
+ {
+ mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw2, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw2->file, mw2->line );
+ if( mwIsReadAddr(mw2->prev,mwDataSize ) )
+ {
+ mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw2->prev, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"",
+ mwIsReadAddr(mw2->file,16)?mw2->file:"<garbage-pointer>", mw2->line );
+ }
+ else
+ {
+ mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n",
+ mw2->prev );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+analyze:
+ if( mwHead == NULL && mwTail == NULL )
+ {
+ mwWrite("relink: both head and tail pointers damaged, aborting program\n");
+ mwFlushW(1);
+ FLUSH();
+ abort();
+ }
+ if( mwHead == NULL )
+ {
+ mwHead = mw2;
+ mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2 );
+ mw2->prev = NULL;
+ mw1 = mw2 = NULL;
+ }
+ if( mwTail == NULL )
+ {
+ mwTail = mw1;
+ mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1 );
+ mw1->next = NULL;
+ mw1 = mw2 = NULL;
+ }
+ if( mw1 == NULL && mw2 == NULL &&
+ mwHead->prev == NULL && mwTail->next == NULL ) {
+ mwWrite("relink: verifying heap integrity...\n" );
+ FLUSH();
+ goto verifyok;
+ }
+ if( mw1 && mw2 && mw1 != mw2 ) {
+ mw1->next = mw2;
+ mw2->prev = mw1;
+ mwWrite("relink: emergency repairs successful, assessing damage...\n");
+ FLUSH();
+ }
+ else {
+ mwWrite("relink: heap totally destroyed, aborting program\n");
+ mwFlushW(1);
+ FLUSH();
+ abort();
+ }
+
+ /* Verify by checking that the number of active allocations */
+ /* match the number of entries in the chain */
+verifyok:
+ if( !mwIsHeapOK( NULL ) ) {
+ mwWrite("relink: heap verification FAILS - aborting program\n");
+ mwFlushW(1);
+ FLUSH();
+ abort();
+ }
+ for( size=count=0, mw1=mwHead; mw1; mw1=mw1->next ) {
+ count ++;
+ size += (long) mw1->size;
+ }
+ if( count == mwNumCurAlloc ) {
+ mwWrite("relink: successful, ");
+ if( size == mwStatCurAlloc ) {
+ mwWrite("no allocations lost\n");
+ }
+ else {
+ if( mw != NULL ) {
+ mwWrite("size information lost for MW-%p\n", mw);
+ mw->size = 0;
+ }
+ }
+ }
+ else {
+ mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n",
+ mwNmlNumAlloc+mwNumCurAlloc-count, mwNmlCurAlloc+mwStatCurAlloc-size );
+ return 0;
+ }
+
+ return 1;
+ }
+
+/*
+** If mwData* is NULL:
+** Returns 0 if heap chain is broken.
+** Returns 1 if heap chain is intact.
+** If mwData* is not NULL:
+** Returns 0 if mwData* is missing or if chain is broken.
+** Returns 1 if chain is intact and mwData* is found.
+*/
+static int mwIsHeapOK( mwData *includes_mw ) {
+ int found = 0;
+ mwData *mw;
+
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( includes_mw == mw ) found++;
+ if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
+ if( mw->prev ) {
+ if( !mwIsSafeAddr( mw->prev, mwDataSize ) ) return 0;
+ if( mw==mwHead || mw->prev->next != mw ) return 0;
+ }
+ if( mw->next ) {
+ if( !mwIsSafeAddr( mw->next, mwDataSize ) ) return 0;
+ if( mw==mwTail || mw->next->prev != mw ) return 0;
+ }
+ else if( mw!=mwTail ) return 0;
+ }
+
+ if( includes_mw != NULL && !found ) return 0;
+
+ return 1;
+ }
+
+static int mwIsOwned( mwData* mw, const char *file, int line ) {
+ int retv;
+ mwStat *ms;
+
+ /* see if the address is legal according to OS */
+ if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
+
+ /* make sure we have _anything_ allocated */
+ if( mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0 )
+ return 0;
+
+ /* calculate checksum */
+ if( mw->check != CHKVAL(mw) ) {
+ /* may be damaged checksum, see if block is in heap */
+ if( mwIsHeapOK( mw ) ) {
+ /* damaged checksum, repair it */
+ mwWrite( "internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n",
+ mwCounter, file, line, mw );
+ mwIncErr();
+ if( mwIsReadAddr( mw->file, 1 ) ) {
+ ms = mwStatGet( mw->file, -1, 0 );
+ if( ms == NULL ) mw->file = "<relinked>";
+ }
+ else mw->file = "<unknown>";
+ mw->size = 0;
+ mw->check = CHKVAL(mw);
+ return 1;
+ }
+ /* no, it's just some garbage data */
+ return 0;
+ }
+
+ /* check that the non-NULL pointers are safe */
+ if( mw->prev && !mwIsSafeAddr( mw->prev, mwDataSize ) ) mwRelink( mw, file, line );
+ if( mw->next && !mwIsSafeAddr( mw->next, mwDataSize ) ) mwRelink( mw, file, line );
+
+ /* safe address, checksum OK, proceed with heap checks */
+
+ /* see if the block is in the heap */
+ retv = 0;
+ if( mw->prev ) { if( mw->prev->next == mw ) retv ++; }
+ else { if( mwHead == mw ) retv++; }
+ if( mw->next ) { if( mw->next->prev == mw ) retv ++; }
+ else { if( mwTail == mw ) retv++; }
+ if( mw->check == CHKVAL(mw) ) retv ++;
+ if( retv > 2 ) return 1;
+
+ /* block not in heap, check heap for corruption */
+
+ if( !mwIsHeapOK( mw ) ) {
+ if( mwRelink( mw, file, line ) )
+ return 1;
+ }
+
+ /* unable to repair */
+ mwWrite( "internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n",
+ mwCounter, file, line, mw );
+ mwIncErr();
+
+ return 0;
+ }
+
+/*
+** mwTestBuf:
+** Checks a buffers links and pre/postfixes.
+** Writes errors found to the log.
+** Returns zero if no errors found.
+*/
+static int mwTestBuf( mwData* mw, const char* file, int line ) {
+ int retv = 0;
+ char *p;
+
+ if( file == NULL ) file = "unknown";
+
+ if( !mwIsSafeAddr( mw, mwDataSize + mwOverflowZoneSize ) ) {
+ mwWrite( "internal: <%ld> %s(%d): pointer MW-%p is invalid\n",
+ mwCounter, file, line, mw );
+ mwIncErr();
+ return 2;
+ }
+
+ if( mw->check != CHKVAL(mw) ) {
+ mwWrite( "internal: <%ld> %s(%d), info trashed; relinking\n",
+ mwCounter, file, line );
+ mwIncErr();
+ if( !mwRelink( mw, file, line ) ) return 2;
+ }
+
+ if( mw->prev && mw->prev->next != mw ) {
+ mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ if( !mwRelink( mw, file, line ) ) retv = 2;
+ }
+ if( mw->next && mw->next->prev != mw ) {
+ mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ if( !mwRelink( mw, file, line ) ) retv = 2;
+ }
+
+ p = ((char*)mw) + mwDataSize;
+ if( mwCheckOF( p ) ) {
+ mwWrite( "underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ retv = 1;
+ }
+ p += mwOverflowZoneSize + mw->size;
+ if( mwIsReadAddr( p, mwOverflowZoneSize ) && mwCheckOF( p ) ) {
+ mwWrite( "overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ retv = 1;
+ }
+
+ return retv;
+ }
+
+static void mwDefaultOutFunc( int c ) {
+ if( mwLogR() ) fputc( c, mwLogR() );
+ }
+
+static void mwWrite( const char *format, ... ) {
+ int tot, oflow = 0;
+ va_list mark;
+ mwAutoInit();
+ if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
+ va_start( mark, format );
+ tot = vsprintf( mwPrintBuf, format, mark );
+ va_end( mark );
+ if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
+ for(tot=0;mwPrintBuf[tot];tot++)
+ (*mwOutFunction)( mwPrintBuf[tot] );
+ if( oflow ) {
+ mwWrite( "\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER-1 );
+ FLUSH();
+ }
+ return;
+ }
+
+static void mwLogFile( const char *name ) {
+ time_t tid;
+ (void) time( &tid );
+ if( mwLogR() != NULL ) {
+ fclose( mwLogR() );
+ mwLogW( NULL );
+ }
+ if( name == NULL ) return;
+ mwLogW( fopen( name, "a" COMMIT ) );
+ if( mwLogR() == NULL )
+ mwWrite( "logfile: failed to open/create file '%s'\n", name );
+ }
+
+/*
+** Try to free NML memory until a contiguous allocation of
+** 'needed' bytes can be satisfied. If this is not enough
+** and the 'urgent' parameter is nonzero, grabbed memory is
+** also freed.
+*/
+static size_t mwFreeUp( size_t needed, int urgent ) {
+ void *p;
+ mwData *mw, *mw2;
+ char *data;
+
+ /* free grabbed NML memory */
+ for(;;) {
+ if( mwDrop_( 1, MW_VAL_NML, 1 ) == 0 ) break;
+ p = malloc( needed );
+ if( p == NULL ) continue;
+ free( p );
+ return needed;
+ }
+
+ /* free normal NML memory */
+ mw = mwHead;
+ while( mw != NULL ) {
+ if( !(mw->flag & MW_NML) ) mw = mw->next;
+ else {
+ data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
+ if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
+ mwIncErr();
+ mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
+ mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
+ }
+ mw2 = mw->next;
+ mwUnlink( mw, "mwFreeUp", 0 );
+ free( mw );
+ mw = mw2;
+ p = malloc( needed );
+ if( p == NULL ) continue;
+ free( p );
+ return needed;
+ }
+ }
+
+ /* if not urgent (for internal purposes), fail */
+ if( !urgent ) return 0;
+
+ /* free grabbed memory */
+ for(;;) {
+ if( mwDrop_( 1, MW_VAL_GRB, 1 ) == 0 ) break;
+ p = malloc( needed );
+ if( p == NULL ) continue;
+ free( p );
+ return needed;
+ }
+
+ return 0;
+ }
+
+static const void * mwTestMem( const void *p, unsigned len, int c ) {
+ const unsigned char *ptr;
+ ptr = (const unsigned char *) p;
+ while( len-- ) {
+ if( *ptr != (unsigned char)c ) return (const void*)ptr;
+ ptr ++;
+ }
+ return NULL;
+ }
+
+static int mwStrCmpI( const char *s1, const char *s2 ) {
+ if( s1 == NULL || s2 == NULL ) return 0;
+ while( *s1 ) {
+ if( toupper(*s2) == toupper(*s1) ) { s1++; s2++; continue; }
+ return 1;
+ }
+ return 0;
+ }
+
+#define AIPH() if( always_invoked ) { mwWrite("autocheck: <%ld> %s(%d) ", mwCounter, file, line ); always_invoked = 0; }
+
+static int mwTestNow( const char *file, int line, int always_invoked ) {
+ int retv = 0;
+ mwData *mw;
+ char *data;
+
+ if( file && !always_invoked )
+ mwWrite("check: <%ld> %s(%d), checking %s%s%s\n",
+ mwCounter, file, line,
+ (mwTestFlags & MW_TEST_CHAIN) ? "chain ": "",
+ (mwTestFlags & MW_TEST_ALLOC) ? "alloc ": "",
+ (mwTestFlags & MW_TEST_NML) ? "nomansland ": ""
+ );
+
+ if( mwTestFlags & MW_TEST_CHAIN ) {
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( !mwIsSafeAddr(mw, mwDataSize) ) {
+ AIPH();
+ mwWrite("check: heap corruption detected\n");
+ mwIncErr();
+ return retv + 1;
+ }
+ if( mw->prev ) {
+ if( !mwIsSafeAddr(mw->prev, mwDataSize) ) {
+ AIPH();
+ mwWrite("check: heap corruption detected\n");
+ mwIncErr();
+ return retv + 1;
+ }
+ if( mw==mwHead || mw->prev->next != mw ) {
+ AIPH();
+ mwWrite("check: heap chain broken, prev link incorrect\n");
+ mwIncErr();
+ retv ++;
+ }
+ }
+ if( mw->next ) {
+ if( !mwIsSafeAddr(mw->next, mwDataSize) ) {
+ AIPH();
+ mwWrite("check: heap corruption detected\n");
+ mwIncErr();
+ return retv + 1;
+ }
+ if( mw==mwTail || mw->next->prev != mw ) {
+ AIPH();
+ mwWrite("check: heap chain broken, next link incorrect\n");
+ mwIncErr();
+ retv ++;
+ }
+ }
+ else if( mw!=mwTail ) {
+ AIPH();
+ mwWrite("check: heap chain broken, tail incorrect\n");
+ mwIncErr();
+ retv ++;
+ }
+ }
+ }
+ if( mwTestFlags & MW_TEST_ALLOC ) {
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( mwTestBuf( mw, file, line ) ) retv ++;
+ }
+ }
+ if( mwTestFlags & MW_TEST_NML ) {
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( (mw->flag & MW_NML) ) {
+ data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
+ if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
+ mwIncErr();
+ mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
+ mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
+ }
+ }
+ }
+ }
+
+
+ if( file && !always_invoked && !retv )
+ mwWrite("check: <%ld> %s(%d), complete; no errors\n",
+ mwCounter, file, line );
+ return retv;
+ }
+
+/**********************************************************************
+** Statistics
+**********************************************************************/
+
+static void mwStatReport()
+{
+ mwStat* ms, *ms2;
+ const char *modname;
+ int modnamelen;
+
+ /* global statistics report */
+ mwWrite( "\nMemory usage statistics (global):\n" );
+ mwWrite( " N)umber of allocations made: %ld\n", mwStatNumAlloc );
+ mwWrite( " L)argest memory usage : %ld\n", mwStatMaxAlloc );
+ mwWrite( " T)otal of all alloc() calls: %ld\n", mwStatTotAlloc );
+ mwWrite( " U)nfreed bytes totals : %ld\n", mwStatCurAlloc );
+ FLUSH();
+
+ if( mwStatLevel < 1 ) return;
+
+ /* on a per-module basis */
+ mwWrite( "\nMemory usage statistics (detailed):\n");
+ mwWrite( " Module/Line Number Largest Total Unfreed \n");
+ for( ms=mwStatList; ms; ms=ms->next )
+ {
+ if( ms->line == -1 )
+ {
+ if( ms->file == NULL || !mwIsReadAddr(ms->file,22) ) modname = "<unknown>";
+ else modname = ms->file;
+ modnamelen = strlen(modname);
+ if( modnamelen > 42 )
+ {
+ modname = modname + modnamelen - 42;
+ }
+
+ mwWrite(" %-42s %-8ld %-8ld %-8ld %-8ld\n",
+ modname, ms->num, ms->max, ms->total, ms->curr );
+ if( ms->file && mwStatLevel > 1 )
+ {
+ for( ms2=mwStatList; ms2; ms2=ms2->next )
+ {
+ if( ms2->line!=-1 && ms2->file!=NULL && !mwStrCmpI( ms2->file, ms->file ) )
+ {
+ mwWrite( " %-8d %-8ld %-8ld %-8ld %-8ld\n",
+ ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr );
+ }
+ }
+ }
+ }
+ }
+}
+
+static mwStat* mwStatGet( const char *file, int line, int makenew ) {
+ mwStat* ms;
+
+ if( mwStatLevel < 2 ) line = -1;
+
+ for( ms=mwStatList; ms!=NULL; ms=ms->next ) {
+ if( line != ms->line ) continue;
+ if( file==NULL ) {
+ if( ms->file == NULL ) break;
+ continue;
+ }
+ if( ms->file == NULL ) continue;
+ if( !strcmp( ms->file, file ) ) break;
+ }
+
+ if( ms != NULL ) return ms;
+
+ if( !makenew ) return NULL;
+
+ ms = (mwStat*) malloc( sizeof(mwStat) );
+ if( ms == NULL ) {
+ if( mwFreeUp( sizeof(mwStat), 0 ) < sizeof(mwStat) ||
+ (ms=(mwStat*)malloc(sizeof(mwStat))) == NULL ) {
+ mwWrite("internal: memory low, statistics incomplete for '%s'\n", file );
+ return NULL;
+ }
+ }
+ ms->file = file;
+ ms->line = line;
+ ms->total = 0L;
+ ms->max = 0L;
+ ms->num = 0L;
+ ms->curr = 0L;
+ ms->next = mwStatList;
+ mwStatList = ms;
+ return ms;
+ }
+
+static void mwStatAlloc( size_t size, const char* file, int line ) {
+ mwStat* ms;
+
+ /* update the module statistics */
+ ms = mwStatGet( file, -1, 1 );
+ if( ms != NULL ) {
+ ms->total += (long) size;
+ ms->curr += (long) size;
+ ms->num ++;
+ if( ms->curr > ms->max ) ms->max = ms->curr;
+ }
+
+ /* update the line statistics */
+ if( mwStatLevel > 1 && line != -1 && file ) {
+ ms = mwStatGet( file, line, 1 );
+ if( ms != NULL ) {
+ ms->total += (long) size;
+ ms->curr += (long) size;
+ ms->num ++;
+ if( ms->curr > ms->max ) ms->max = ms->curr;
+ }
+ }
+
+ }
+
+static void mwStatFree( size_t size, const char* file, int line ) {
+ mwStat* ms;
+
+ /* update the module statistics */
+ ms = mwStatGet( file, -1, 1 );
+ if( ms != NULL ) ms->curr -= (long) size;
+
+ /* update the line statistics */
+ if( mwStatLevel > 1 && line != -1 && file ) {
+ ms = mwStatGet( file, line, 1 );
+ if( ms != NULL ) ms->curr -= (long) size;
+ }
+ }
+
+/***********************************************************************
+** Safe memory checkers
+**
+** Using ifdefs, implement the operating-system specific mechanism
+** of identifying a piece of memory as legal to access with read
+** and write priviliges. Default: return nonzero for non-NULL pointers.
+***********************************************************************/
+
+static char mwDummy( char c )
+{
+ return c;
+}
+
+#ifndef MW_SAFEADDR
+#ifdef WIN32
+#define MW_SAFEADDR
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+int mwIsReadAddr( const void *p, unsigned len )
+{
+ if( p == NULL ) return 0;
+ if( IsBadReadPtr(p,len) ) return 0;
+ return 1;
+}
+int mwIsSafeAddr( void *p, unsigned len )
+{
+ /* NOTE: For some reason, under Win95 the IsBad... */
+ /* can return false for invalid pointers. */
+ if( p == NULL ) return 0;
+ if( IsBadReadPtr(p,len) || IsBadWritePtr(p,len) ) return 0;
+ return 1;
+}
+#endif /* WIN32 */
+#endif /* MW_SAFEADDR */
+
+#ifndef MW_SAFEADDR
+#ifdef SIGSEGV
+#define MW_SAFEADDR
+
+typedef void (*mwSignalHandlerPtr)( int );
+mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0;
+jmp_buf mwSIGSEGVjump;
+static void mwSIGSEGV( int n );
+
+static void mwSIGSEGV( int n )
+{
+ n = n;
+ longjmp( mwSIGSEGVjump, 1 );
+}
+
+int mwIsReadAddr( const void *p, unsigned len )
+{
+ const char *ptr;
+
+ if( p == NULL ) return 0;
+ if( !len ) return 1;
+
+ /* set up to catch the SIGSEGV signal */
+ mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
+
+ if( setjmp( mwSIGSEGVjump ) )
+ {
+ signal( SIGSEGV, mwOldSIGSEGV );
+ return 0;
+ }
+
+ /* read all the bytes in the range */
+ ptr = (const char *)p;
+ ptr += len;
+
+ /* the reason for this rather strange construct is that */
+ /* we want to keep the number of used parameters and locals */
+ /* to a minimum. if we use len for a counter gcc will complain */
+ /* it may get clobbered by longjmp() at high warning levels. */
+ /* it's a harmless warning, but this way we don't have to see it. */
+ do
+ {
+ ptr --;
+ if( *ptr == 0x7C ) (void) mwDummy( (char)0 );
+ } while( (const void*) ptr != p );
+
+ /* remove the handler */
+ signal( SIGSEGV, mwOldSIGSEGV );
+
+ return 1;
+}
+int mwIsSafeAddr( void *p, unsigned len )
+{
+ char *ptr;
+
+ if( p == NULL ) return 0;
+ if( !len ) return 1;
+
+ /* set up to catch the SIGSEGV signal */
+ mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
+
+ if( setjmp( mwSIGSEGVjump ) )
+ {
+ signal( SIGSEGV, mwOldSIGSEGV );
+ return 0;
+ }
+
+ /* read and write-back all the bytes in the range */
+ ptr = (char *)p;
+ ptr += len;
+
+ /* the reason for this rather strange construct is that */
+ /* we want to keep the number of used parameters and locals */
+ /* to a minimum. if we use len for a counter gcc will complain */
+ /* it may get clobbered by longjmp() at high warning levels. */
+ /* it's a harmless warning, but this way we don't have to see it. */
+ do
+ {
+ ptr --;
+ *ptr = mwDummy( *ptr );
+ } while( (void*) ptr != p );
+
+ /* remove the handler */
+ signal( SIGSEGV, mwOldSIGSEGV );
+
+ return 1;
+}
+#endif /* SIGSEGV */
+#endif /* MW_SAFEADDR */
+
+#ifndef MW_SAFEADDR
+int mwIsReadAddr( const void *p, unsigned len )
+{
+ if( p == NULL ) return 0;
+ if( len == 0 ) return 1;
+ return 1;
+}
+int mwIsSafeAddr( void *p, unsigned len )
+{
+ if( p == NULL ) return 0;
+ if( len == 0 ) return 1;
+ return 1;
+}
+#endif
+
+/**********************************************************************
+** Mutex handling
+**********************************************************************/
+
+#if defined(WIN32) || defined(__WIN32__)
+
+static void mwMutexInit( void )
+{
+ mwGlobalMutex = CreateMutex( NULL, FALSE, NULL);
+ return;
+}
+
+static void mwMutexTerm( void )
+{
+ CloseHandle( mwGlobalMutex );
+ return;
+}
+
+static void mwMutexLock( void )
+{
+ if( WaitForSingleObject(mwGlobalMutex, 1000 ) == WAIT_TIMEOUT )
+ {
+ mwWrite( "mwMutexLock: timed out, possible deadlock\n" );
+ }
+ return;
+}
+
+static void mwMutexUnlock( void )
+{
+ ReleaseMutex( mwGlobalMutex );
+ return;
+}
+
+#endif
+
+#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
+
+static void mwMutexInit( void )
+{
+ pthread_mutex_init( &mwGlobalMutex, NULL );
+ return;
+}
+
+static void mwMutexTerm( void )
+{
+ pthread_mutex_destroy( &mwGlobalMutex );
+ return;
+}
+
+static void mwMutexLock( void )
+{
+ pthread_mutex_lock(&mwGlobalMutex);
+ return;
+}
+
+static void mwMutexUnlock( void )
+{
+ pthread_mutex_unlock(&mwGlobalMutex);
+ return;
+}
+
+#endif
+
+/**********************************************************************
+** C++ new & delete
+**********************************************************************/
+
+#if 0 /* 980317: disabled C++ */
+
+#ifdef __cplusplus
+#ifndef MEMWATCH_NOCPP
+
+int mwNCur = 0;
+const char *mwNFile = NULL;
+int mwNLine = 0;
+
+class MemWatch {
+public:
+ MemWatch();
+ ~MemWatch();
+ };
+
+MemWatch::MemWatch() {
+ if( mwInited ) return;
+ mwUseAtexit = 0;
+ mwInit();
+ }
+
+MemWatch::~MemWatch() {
+ if( mwUseAtexit ) return;
+ mwTerm();
+ }
+
+/*
+** This global new will catch all 'new' calls where MEMWATCH is
+** not active.
+*/
+void* operator new( unsigned size ) {
+ mwNCur = 0;
+ return mwMalloc( size, "<unknown>", 0 );
+ }
+
+/*
+** This is the new operator that's called when a module uses mwNew.
+*/
+void* operator new( unsigned size, const char *file, int line ) {
+ mwNCur = 0;
+ return mwMalloc( size, file, line );
+ }
+
+/*
+** This is the new operator that's called when a module uses mwNew[].
+** -- hjc 07/16/02
+*/
+void* operator new[] ( unsigned size, const char *file, int line ) {
+ mwNCur = 0;
+ return mwMalloc( size, file, line );
+ }
+
+/*
+** Since this delete operator will recieve ALL delete's
+** even those from within libraries, we must accept
+** delete's before we've been initialized. Nor can we
+** reliably check for wild free's if the mwNCur variable
+** is not set.
+*/
+void operator delete( void *p ) {
+ if( p == NULL ) return;
+ if( !mwInited ) {
+ free( p );
+ return;
+ }
+ if( mwNCur ) {
+ mwFree( p, mwNFile, mwNLine );
+ mwNCur = 0;
+ return;
+ }
+ mwFree_( p );
+ }
+
+void operator delete[]( void *p ) {
+ if( p == NULL ) return;
+ if( !mwInited ) {
+ free( p );
+ return;
+ }
+ if( mwNCur ) {
+ mwFree( p, mwNFile, mwNLine );
+ mwNCur = 0;
+ return;
+ }
+ mwFree_( p );
+ }
+
+#endif /* MEMWATCH_NOCPP */
+#endif /* __cplusplus */
+
+#endif /* 980317: disabled C++ */
+
+/* MEMWATCH.C */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/memwatch.h b/plugins/!NotAdopted/VypressChat/contrib/memwatch.h
new file mode 100644
index 0000000000..c60d31b7e8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/memwatch.h
@@ -0,0 +1,707 @@
+/*
+** MEMWATCH.H
+** Nonintrusive ANSI C memory leak / overwrite detection
+** Copyright (C) 1992-2002 Johan Lindh
+** All rights reserved.
+** Version 2.71
+**
+************************************************************************
+**
+** PURPOSE:
+**
+** MEMWATCH has been written to allow guys and gals that like to
+** program in C a public-domain memory error control product.
+** I hope you'll find it's as advanced as most commercial packages.
+** The idea is that you use it during the development phase and
+** then remove the MEMWATCH define to produce your final product.
+** MEMWATCH is distributed in source code form in order to allow
+** you to compile it for your platform with your own compiler.
+** It's aim is to be 100% ANSI C, but some compilers are more stingy
+** than others. If it doesn't compile without warnings, please mail
+** me the configuration of operating system and compiler you are using
+** along with a description of how to modify the source, and the version
+** number of MEMWATCH that you are using.
+**
+************************************************************************
+
+ This file is part of MEMWATCH.
+
+ MEMWATCH is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ MEMWATCH is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with MEMWATCH; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+************************************************************************
+**
+** REVISION HISTORY:
+**
+** 920810 JLI [1.00]
+** 920830 JLI [1.10 double-free detection]
+** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit]
+** 921022 JLI [1.20 ASSERT and VERIFY]
+** 921105 JLI [1.30 C++ support and TRACE]
+** 921116 JLI [1.40 mwSetOutFunc]
+** 930215 JLI [1.50 modified ASSERT/VERIFY]
+** 930327 JLI [1.51 better auto-init & PC-lint support]
+** 930506 JLI [1.55 MemWatch class, improved C++ support]
+** 930507 JLI [1.60 mwTest & CHECK()]
+** 930809 JLI [1.65 Abort/Retry/Ignore]
+** 930820 JLI [1.70 data dump when unfreed]
+** 931016 JLI [1.72 modified C++ new/delete handling]
+** 931108 JLI [1.77 mwSetAssertAction() & some small changes]
+** 940110 JLI [1.80 no-mans-land alloc/checking]
+** 940328 JLI [2.00 version 2.0 rewrite]
+** Improved NML (no-mans-land) support.
+** Improved performance (especially for free()ing!).
+** Support for 'read-only' buffers (checksums)
+** ^^ NOTE: I never did this... maybe I should?
+** FBI (free'd block info) tagged before freed blocks
+** Exporting of the mwCounter variable
+** mwBreakOut() localizes debugger support
+** Allocation statistics (global, per-module, per-line)
+** Self-repair ability with relinking
+** 950913 JLI [2.10 improved garbage handling]
+** 951201 JLI [2.11 improved auto-free in emergencies]
+** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()]
+** 960514 JLI [2.12 undefining of existing macros]
+** 960515 JLI [2.13 possibility to use default new() & delete()]
+** 960516 JLI [2.20 suppression of file flushing on unfreed msgs]
+** 960516 JLI [2.21 better support for using MEMWATCH with DLL's]
+** 960710 JLI [X.02 multiple logs and mwFlushNow()]
+** 960801 JLI [2.22 merged X.01 version with current]
+** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
+** 960805 JLI [2.31 merged X.02 version with current]
+** 961002 JLI [2.32 support for realloc() + fixed STDERR bug]
+** 961222 JLI [2.40 added mwMark() & mwUnmark()]
+** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY]
+** 970113 JLI [2.42 added support for PC-Lint 7.00g]
+** 970207 JLI [2.43 added support for strdup()]
+** 970209 JLI [2.44 changed default filename to lowercase]
+** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers]
+** 970723 JLI [2.46 added MW_ARI_NULLREAD flag]
+** 970813 JLI [2.47 stabilized marker handling]
+** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway]
+** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support]
+** 980417 JLI [2.51 more checks for invalid addresses]
+** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting]
+** 990112 JLI [2.53 added check for empty heap to mwIsOwned]
+** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML]
+** 990224 JLI [2.56 changed ordering of members in structures]
+** 990303 JLI [2.57 first maybe-fixit-for-hpux test]
+** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit]
+** 990517 JLI [2.59 fixed some high-sensitivity warnings]
+** 990610 JLI [2.60 fixed some more high-sensitivity warnings]
+** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names]
+** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()]
+** 991007 JLI [2.63 first shot at a 64-bit compatible version]
+** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const]
+** 000704 JLI [2.65 added some more detection for 64-bits]
+** 010502 JLI [2.66 incorporated some user fixes]
+** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)]
+** [added array destructor for C++ (thanks rdasilva@connecttel.com)]
+** [added mutex support (thanks rdasilva@connecttel.com)]
+** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined]
+** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked]
+** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen]
+** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)]
+** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)]
+**
+** To use, simply include 'MEMWATCH.H' as a header file,
+** and add MEMWATCH.C to your list of files, and define the macro
+** 'MEMWATCH'. If this is not defined, MEMWATCH will disable itself.
+**
+** To call the standard C malloc / realloc / calloc / free; use mwMalloc_(),
+** mwCalloc_() and mwFree_(). Note that mwFree_() will correctly
+** free both malloc()'d memory as well as mwMalloc()'d.
+**
+** 980317: C++ support has been disabled.
+** The code remains, but is not compiled.
+**
+** For use with C++, which allows use of inlining in header files
+** and class specific new/delete, you must also define 'new' as
+** 'mwNew' and 'delete' as 'mwDelete'. Do this *after* you include
+** C++ header files from libraries, otherwise you can mess up their
+** class definitions. If you don't define these, the C++ allocations
+** will not have source file and line number information. Also note,
+** most C++ class libraries implement their own C++ memory management,
+** and don't allow anyone to override them. MFC belongs to this crew.
+** In these cases, the only thing to do is to use MEMWATCH_NOCPP.
+**
+** You can capture output from MEMWATCH using mwSetOutFunc().
+** Just give it the adress of a "void myOutFunc(int c)" function,
+** and all characters to be output will be redirected there.
+**
+** A failing ASSERT() or VERIFY() will normally always abort your
+** program. This can be changed using mwSetAriFunc(). Give it a
+** pointer to a "int myAriFunc(const char *)" function. Your function
+** must ask the user whether to Abort, Retry or Ignore the trap.
+** Return 2 to Abort, 1 to Retry or 0 to Ignore. Beware retry; it
+** causes the expression to be evaluated again! MEMWATCH has a
+** default ARI handler. It's disabled by default, but you can enable
+** it by calling 'mwDefaultAri()'. Note that this will STILL abort
+** your program unless you define MEMWATCH_STDIO to allow MEMWATCH
+** to use the standard C I/O streams. Also, setting the ARI function
+** will cause MEMWATCH *NOT* to write the ARI error to stderr. The
+** error string is passed to the ARI function instead, as the
+** 'const char *' parameter.
+**
+** You can disable MEMWATCH's ASSERT/VERIFY and/or TRACE implementations.
+** This can be useful if you're using a debug terminal or smart debugger.
+** Disable them by defining MW_NOASSERT, MW_NOVERIFY or MW_NOTRACE.
+**
+** MEMWATCH fills all allocated memory with the byte 0xFE, so if
+** you're looking at erroneous data which are all 0xFE:s, the
+** data probably was not initialized by you. The exception is
+** calloc(), which will fill with zero's. All freed buffers are
+** zapped with 0xFD. If this is what you look at, you're using
+** data that has been freed. If this is the case, be aware that
+** MEMWATCH places a 'free'd block info' structure immediately
+** before the freed data. This block contains info about where
+** the block was freed. The information is in readable text,
+** in the format "FBI<counter>filename(line)", for example:
+** "FBI<267>test.c(12)". Using FBI's slows down free(), so it's
+** disabled by default. Use mwFreeBufferInfo(1) to enable it.
+**
+** To aid in tracking down wild pointer writes, MEMWATCH can perform
+** no-mans-land allocations. No-mans-land will contain the byte 0xFC.
+** MEMWATCH will, when this is enabled, convert recently free'd memory
+** into NML allocations.
+**
+** MEMWATCH protects it's own data buffers with checksums. If you
+** get an internal error, it means you're overwriting wildly,
+** or using an uninitialized pointer.
+**
+************************************************************************
+**
+** Note when compiling with Microsoft C:
+** - MSC ignores fflush() by default. This is overridden, so that
+** the disk log will always be current.
+**
+** This utility has been tested with:
+** PC-lint 7.0k, passed as 100% ANSI C compatible
+** Microsoft Visual C++ on Win16 and Win32
+** Microsoft C on DOS
+** SAS C on an Amiga 500
+** Gnu C on a PC running Red Hat Linux
+** ...and using an (to me) unknown compiler on an Atari machine.
+**
+************************************************************************
+**
+** Format of error messages in MEMWATCH.LOG:
+** message: <sequence-number> filename(linenumber), information
+**
+** Errors caught by MemWatch, when they are detected, and any
+** actions taken besides writing to the log file MEMWATCH.LOG:
+**
+** Double-freeing:
+** A pointer that was recently freed and has not since been
+** reused was freed again. The place where the previous free()
+** was executed is displayed.
+** Detect: delete or free() using the offending pointer.
+** Action: The delete or free() is cancelled, execution continues.
+** Underflow:
+** You have written just ahead of the allocated memory.
+** The size and place of the allocation is displayed.
+** Detect: delete or free() of the damaged buffer.
+** Action: The buffer is freed, but there may be secondary damage.
+** Overflow:
+** Like underflow, but you've written after the end of the buffer.
+** Detect: see Underflow.
+** Action: see Underflow.
+** WILD free:
+** An unrecognized pointer was passed to delete or free().
+** The pointer may have been returned from a library function;
+** in that case, use mwFree_() to force free() of it.
+** Also, this may be a double-free, but the previous free was
+** too long ago, causing MEMWATCH to 'forget' it.
+** Detect: delete or free() of the offending pointer.
+** Action: The delete or free() is cancelled, execution continues.
+** NULL free:
+** It's unclear to me whether or not freeing of NULL pointers
+** is legal in ANSI C, therefore a warning is written to the log file,
+** but the error counter remains the same. This is legal using C++,
+** so the warning does not appear with delete.
+** Detect: When you free(NULL).
+** Action: The free() is cancelled.
+** Failed:
+** A request to allocate memory failed. If the allocation is
+** small, this may be due to memory depletion, but is more likely
+** to be memory fragmentation problems. The amount of memory
+** allocated so far is displayed also.
+** Detect: When you new, malloc(), realloc() or calloc() memory.
+** Action: NULL is returned.
+** Realloc:
+** A request to re-allocate a memory buffer failed for reasons
+** other than out-of-memory. The specific reason is shown.
+** Detect: When you realloc()
+** Action: realloc() is cancelled, NULL is returned
+** Limit fail:
+** A request to allocate memory failed since it would violate
+** the limit set using mwLimit(). mwLimit() is used to stress-test
+** your code under simulated low memory conditions.
+** Detect: At new, malloc(), realloc() or calloc().
+** Action: NULL is returned.
+** Assert trap:
+** An ASSERT() failed. The ASSERT() macro works like C's assert()
+** macro/function, except that it's interactive. See your C manual.
+** Detect: On the ASSERT().
+** Action: Program ends with an advisory message to stderr, OR
+** Program writes the ASSERT to the log and continues, OR
+** Program asks Abort/Retry/Ignore? and takes that action.
+** Verify trap:
+** A VERIFY() failed. The VERIFY() macro works like ASSERT(),
+** but if MEMWATCH is not defined, it still evaluates the
+** expression, but it does not act upon the result.
+** Detect: On the VERIFY().
+** Action: Program ends with an advisory message to stderr, OR
+** Program writes the VERIFY to the log and continues, OR
+** Program asks Abort/Retry/Ignore? and takes that action.
+** Wild pointer:
+** A no-mans-land buffer has been written into. MEMWATCH can
+** allocate and distribute chunks of memory solely for the
+** purpose of trying to catch random writes into memory.
+** Detect: Always on CHECK(), but can be detected in several places.
+** Action: The error is logged, and if an ARI handler is installed,
+** it is executed, otherwise, execution continues.
+** Unfreed:
+** A memory buffer you allocated has not been freed.
+** You are informed where it was allocated, and whether any
+** over or underflow has occured. MemWatch also displays up to
+** 16 bytes of the data, as much as it can, in hex and text.
+** Detect: When MemWatch terminates.
+** Action: The buffer is freed.
+** Check:
+** An error was detected during a CHECK() operation.
+** The associated pointer is displayed along with
+** the file and line where the CHECK() was executed.
+** Followed immediately by a normal error message.
+** Detect: When you CHECK()
+** Action: Depends on the error
+** Relink:
+** After a MEMWATCH internal control block has been trashed,
+** MEMWATCH tries to repair the damage. If successful, program
+** execution will continue instead of aborting. Some information
+** about the block may be gone permanently, though.
+** Detect: N/A
+** Action: Relink successful: program continues.
+** Relink fails: program aborts.
+** Internal:
+** An internal error is flagged by MEMWATCH when it's control
+** structures have been damaged. You are likely using an uninitialized
+** pointer somewhere in your program, or are zapping memory all over.
+** The message may give you additional diagnostic information.
+** If possible, MEMWATCH will recover and continue execution.
+** Detect: Various actions.
+** Action: Whatever is needed
+** Mark:
+** The program terminated without umarking all marked pointers. Marking
+** can be used to track resources other than memory. mwMark(pointer,text,...)
+** when the resource is allocated, and mwUnmark(pointer) when it's freed.
+** The 'text' is displayed for still marked pointers when the program
+** ends.
+** Detect: When MemWatch terminates.
+** Action: The error is logged.
+**
+**
+************************************************************************
+**
+** The author may be reached by e-mail at the address below. If you
+** mail me about source code changes in MEMWATCH, remember to include
+** MW's version number.
+**
+** Johan Lindh
+** johan@linkdata.se
+**
+** The latest version of MEMWATCH may be downloaded from
+** http://www.linkdata.se/
+*/
+
+#ifndef __MEMWATCH_H
+#define __MEMWATCH_H
+
+/* Make sure that malloc(), realloc(), calloc() and free() are declared. */
+/*lint -save -e537 */
+#include <stdlib.h>
+/*lint -restore */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+** Constants used
+** All MEMWATCH constants start with the prefix MW_, followed by
+** a short mnemonic which indicates where the constant is used,
+** followed by a descriptive text about it.
+*/
+
+#define MW_ARI_NULLREAD 0x10 /* Null read (to start debugger) */
+#define MW_ARI_ABORT 0x04 /* ARI handler says: abort program! */
+#define MW_ARI_RETRY 0x02 /* ARI handler says: retry action! */
+#define MW_ARI_IGNORE 0x01 /* ARI handler says: ignore error! */
+
+#define MW_VAL_NEW 0xFE /* value in newly allocated memory */
+#define MW_VAL_DEL 0xFD /* value in newly deleted memory */
+#define MW_VAL_NML 0xFC /* value in no-mans-land */
+#define MW_VAL_GRB 0xFB /* value in grabbed memory */
+
+#define MW_TEST_ALL 0xFFFF /* perform all tests */
+#define MW_TEST_CHAIN 0x0001 /* walk the heap chain */
+#define MW_TEST_ALLOC 0x0002 /* test allocations & NML guards */
+#define MW_TEST_NML 0x0004 /* test all-NML areas for modifications */
+
+#define MW_NML_NONE 0 /* no NML */
+#define MW_NML_FREE 1 /* turn FREE'd memory into NML */
+#define MW_NML_ALL 2 /* all unused memory is NML */
+#define MW_NML_DEFAULT 0 /* the default NML setting */
+
+#define MW_STAT_GLOBAL 0 /* only global statistics collected */
+#define MW_STAT_MODULE 1 /* collect statistics on a module basis */
+#define MW_STAT_LINE 2 /* collect statistics on a line basis */
+#define MW_STAT_DEFAULT 0 /* the default statistics setting */
+
+/*
+** MemWatch internal constants
+** You may change these and recompile MemWatch to change the limits
+** of some parameters. Respect the recommended minimums!
+*/
+#define MW_TRACE_BUFFER 2048 /* (min 160) size of TRACE()'s output buffer */
+#define MW_FREE_LIST 64 /* (min 4) number of free()'s to track */
+
+/*
+** Exported variables
+** In case you have to remove the 'const' keyword because your compiler
+** doesn't support it, be aware that changing the values may cause
+** unpredictable behaviour.
+** - mwCounter contains the current action count. You can use this to
+** place breakpoints using a debugger, if you want.
+*/
+#ifndef __MEMWATCH_C
+extern const unsigned long mwCounter;
+#endif
+
+/*
+** System functions
+** Normally, it is not nessecary to call any of these. MEMWATCH will
+** automatically initialize itself on the first MEMWATCH function call,
+** and set up a call to mwAbort() using atexit(). Some C++ implementations
+** run the atexit() chain before the program has terminated, so you
+** may have to use mwInit() or the MemWatch C++ class to get good
+** behaviour.
+** - mwInit() can be called to disable the atexit() usage. If mwInit()
+** is called directly, you must call mwTerm() to end MemWatch, or
+** mwAbort().
+** - mwTerm() is usually not nessecary to call; but if called, it will
+** call mwAbort() if it finds that it is cancelling the 'topmost'
+** mwInit() call.
+** - mwAbort() cleans up after MEMWATCH, reports unfreed buffers, etc.
+*/
+void mwInit( void );
+void mwTerm( void );
+void mwAbort( void );
+
+/*
+** Setup functions
+** These functions control the operation of MEMWATCH's protective features.
+** - mwFlushNow() causes MEMWATCH to flush it's buffers.
+** - mwDoFlush() controls whether MEMWATCH flushes the disk buffers after
+** writes. The default is smart flushing: MEMWATCH will not flush buffers
+** explicitly until memory errors are detected. Then, all writes are
+** flushed until program end or mwDoFlush(0) is called.
+** - mwLimit() sets the allocation limit, an arbitrary limit on how much
+** memory your program may allocate in bytes. Used to stress-test app.
+** Also, in virtual-memory or multitasking environs, puts a limit on
+** how much MW_NML_ALL can eat up.
+** - mwGrab() grabs up X kilobytes of memory. Allocates actual memory,
+** can be used to stress test app & OS both.
+** - mwDrop() drops X kilobytes of grabbed memory.
+** - mwNoMansLand() sets the behaviour of the NML logic. See the
+** MW_NML_xxx for more information. The default is MW_NML_DEFAULT.
+** - mwStatistics() sets the behaviour of the statistics collector. See
+** the MW_STAT_xxx defines for more information. Default MW_STAT_DEFAULT.
+** - mwFreeBufferInfo() enables or disables the tagging of free'd buffers
+** with freeing information. This information is written in text form,
+** using sprintf(), so it's pretty slow. Disabled by default.
+** - mwAutoCheck() performs a CHECK() operation whenever a MemWatch function
+** is used. Slows down performance, of course.
+** - mwCalcCheck() calculates checksums for all data buffers. Slow!
+** - mwDumpCheck() logs buffers where stored & calc'd checksums differ. Slow!!
+** - mwMark() sets a generic marker. Returns the pointer given.
+** - mwUnmark() removes a generic marker. If, at the end of execution, some
+** markers are still in existence, these will be reported as leakage.
+** returns the pointer given.
+*/
+void mwFlushNow( void );
+void mwDoFlush( int onoff );
+void mwLimit( long bytes );
+unsigned mwGrab( unsigned kilobytes );
+unsigned mwDrop( unsigned kilobytes );
+void mwNoMansLand( int mw_nml_level );
+void mwStatistics( int level );
+void mwFreeBufferInfo( int onoff );
+void mwAutoCheck( int onoff );
+void mwCalcCheck( void );
+void mwDumpCheck( void );
+void * mwMark( void *p, const char *description, const char *file, unsigned line );
+void * mwUnmark( void *p, const char *file, unsigned line );
+
+/*
+** Testing/verification/tracing
+** All of these macros except VERIFY() evaluates to a null statement
+** if MEMWATCH is not defined during compilation.
+** - mwIsReadAddr() checks a memory area for read privilige.
+** - mwIsSafeAddr() checks a memory area for both read & write privilige.
+** This function and mwIsReadAddr() is highly system-specific and
+** may not be implemented. If this is the case, they will default
+** to returning nonzero for any non-NULL pointer.
+** - CHECK() does a complete memory integrity test. Slow!
+** - CHECK_THIS() checks only selected components.
+** - CHECK_BUFFER() checks the indicated buffer for errors.
+** - mwASSERT() or ASSERT() If the expression evaluates to nonzero, execution continues.
+** Otherwise, the ARI handler is called, if present. If not present,
+** the default ARI action is taken (set with mwSetAriAction()).
+** ASSERT() can be disabled by defining MW_NOASSERT.
+** - mwVERIFY() or VERIFY() works just like ASSERT(), but when compiling without
+** MEMWATCH the macro evaluates to the expression.
+** VERIFY() can be disabled by defining MW_NOVERIFY.
+** - mwTRACE() or TRACE() writes some text and data to the log. Use like printf().
+** TRACE() can be disabled by defining MW_NOTRACE.
+*/
+int mwIsReadAddr( const void *p, unsigned len );
+int mwIsSafeAddr( void *p, unsigned len );
+int mwTest( const char *file, int line, int mw_test_flags );
+int mwTestBuffer( const char *file, int line, void *p );
+int mwAssert( int, const char*, const char*, int );
+int mwVerify( int, const char*, const char*, int );
+
+/*
+** User I/O functions
+** - mwTrace() works like printf(), but dumps output either to the
+** function specified with mwSetOutFunc(), or the log file.
+** - mwPuts() works like puts(), dumps output like mwTrace().
+** - mwSetOutFunc() allows you to give the adress of a function
+** where all user output will go. (exeption: see mwSetAriFunc)
+** Specifying NULL will direct output to the log file.
+** - mwSetAriFunc() gives MEMWATCH the adress of a function to call
+** when an 'Abort, Retry, Ignore' question is called for. The
+** actual error message is NOT printed when you've set this adress,
+** but instead it is passed as an argument. If you call with NULL
+** for an argument, the ARI handler is disabled again. When the
+** handler is disabled, MEMWATCH will automatically take the
+** action specified by mwSetAriAction().
+** - mwSetAriAction() sets the default ARI return value MEMWATCH should
+** use if no ARI handler is specified. Defaults to MW_ARI_ABORT.
+** - mwAriHandler() is an ANSI ARI handler you can use if you like. It
+** dumps output to stderr, and expects input from stdin.
+** - mwBreakOut() is called in certain cases when MEMWATCH feels it would
+** be nice to break into a debugger. If you feel like MEMWATCH, place
+** an execution breakpoint on this function.
+*/
+void mwTrace( const char* format_string, ... );
+void mwPuts( const char* text );
+void mwSetOutFunc( void (*func)(int) );
+void mwSetAriFunc( int (*func)(const char*) );
+void mwSetAriAction( int mw_ari_value );
+int mwAriHandler( const char* cause );
+void mwBreakOut( const char* cause );
+
+/*
+** Allocation/deallocation functions
+** These functions are the ones actually to perform allocations
+** when running MEMWATCH, for both C and C++ calls.
+** - mwMalloc() debugging allocator
+** - mwMalloc_() always resolves to a clean call of malloc()
+** - mwRealloc() debugging re-allocator
+** - mwRealloc_() always resolves to a clean call of realloc()
+** - mwCalloc() debugging allocator, fills with zeros
+** - mwCalloc_() always resolves to a clean call of calloc()
+** - mwFree() debugging free. Can only free memory which has
+** been allocated by MEMWATCH.
+** - mwFree_() resolves to a) normal free() or b) debugging free.
+** Can free memory allocated by MEMWATCH and malloc() both.
+** Does not generate any runtime errors.
+*/
+void* mwMalloc( size_t, const char*, int );
+void* mwMalloc_( size_t );
+void* mwRealloc( void *, size_t, const char*, int );
+void* mwRealloc_( void *, size_t );
+void* mwCalloc( size_t, size_t, const char*, int );
+void* mwCalloc_( size_t, size_t );
+void mwFree( void*, const char*, int );
+void mwFree_( void* );
+char* mwStrdup( const char *, const char*, int );
+
+/*
+** Enable/disable precompiler block
+** This block of defines and if(n)defs make sure that references
+** to MEMWATCH is completely removed from the code if the MEMWATCH
+** manifest constant is not defined.
+*/
+#ifndef __MEMWATCH_C
+#ifdef MEMWATCH
+
+#define mwASSERT(exp) while(mwAssert((int)(exp),#exp,__FILE__,__LINE__))
+#ifndef MW_NOASSERT
+#ifndef ASSERT
+#define ASSERT mwASSERT
+#endif /* !ASSERT */
+#endif /* !MW_NOASSERT */
+#define mwVERIFY(exp) while(mwVerify((int)(exp),#exp,__FILE__,__LINE__))
+#ifndef MW_NOVERIFY
+#ifndef VERIFY
+#define VERIFY mwVERIFY
+#endif /* !VERIFY */
+#endif /* !MW_NOVERIFY */
+#define mwTRACE mwTrace
+#ifndef MW_NOTRACE
+#ifndef TRACE
+#define TRACE mwTRACE
+#endif /* !TRACE */
+#endif /* !MW_NOTRACE */
+
+/* some compilers use a define and not a function */
+/* for strdup(). */
+#ifdef strdup
+#undef strdup
+#endif
+
+#define malloc(n) mwMalloc(n,__FILE__,__LINE__)
+#define strdup(p) mwStrdup(p,__FILE__,__LINE__)
+#define realloc(p,n) mwRealloc(p,n,__FILE__,__LINE__)
+#define calloc(n,m) mwCalloc(n,m,__FILE__,__LINE__)
+#define free(p) mwFree(p,__FILE__,__LINE__)
+#define CHECK() mwTest(__FILE__,__LINE__,MW_TEST_ALL)
+#define CHECK_THIS(n) mwTest(__FILE__,__LINE__,n)
+#define CHECK_BUFFER(b) mwTestBuffer(__FILE__,__LINE__,b)
+#define MARK(p) mwMark(p,#p,__FILE__,__LINE__)
+#define UNMARK(p) mwUnmark(p,__FILE__,__LINE__)
+
+#else /* MEMWATCH */
+
+#define mwASSERT(exp)
+#ifndef MW_NOASSERT
+#ifndef ASSERT
+#define ASSERT mwASSERT
+#endif /* !ASSERT */
+#endif /* !MW_NOASSERT */
+
+#define mwVERIFY(exp) exp
+#ifndef MW_NOVERIFY
+#ifndef VERIFY
+#define VERIFY mwVERIFY
+#endif /* !VERIFY */
+#endif /* !MW_NOVERIFY */
+
+/*lint -esym(773,mwTRACE) */
+#define mwTRACE /*lint -save -e506 */ 1?(void)0:mwDummyTraceFunction /*lint -restore */
+#ifndef MW_NOTRACE
+#ifndef TRACE
+/*lint -esym(773,TRACE) */
+#define TRACE mwTRACE
+#endif /* !TRACE */
+#endif /* !MW_NOTRACE */
+
+extern void mwDummyTraceFunction(const char *,...);
+/*lint -save -e652 */
+#define mwDoFlush(n)
+#define mwPuts(s)
+#define mwInit()
+#define mwGrab(n)
+#define mwDrop(n)
+#define mwLimit(n)
+#define mwTest(f,l)
+#define mwSetOutFunc(f)
+#define mwSetAriFunc(f)
+#define mwDefaultAri()
+#define mwNomansland()
+#define mwStatistics(f)
+#define mwMark(p,t,f,n) (p)
+#define mwUnmark(p,f,n) (p)
+#define mwMalloc(n,f,l) malloc(n)
+#define mwStrdup(p,f,l) strdup(p)
+#define mwRealloc(p,n,f,l) realloc(p,n)
+#define mwCalloc(n,m,f,l) calloc(n,m)
+#define mwFree(p) free(p)
+#define mwMalloc_(n) malloc(n)
+#define mwRealloc_(p,n) realloc(p,n)
+#define mwCalloc_(n,m) calloc(n,m)
+#define mwFree_(p) free(p)
+#define mwAssert(e,es,f,l)
+#define mwVerify(e,es,f,l) (e)
+#define mwTrace mwDummyTrace
+#define mwTestBuffer(f,l,b) (0)
+#define CHECK()
+#define CHECK_THIS(n)
+#define CHECK_BUFFER(b)
+#define MARK(p) (p)
+#define UNMARK(p) (p)
+/*lint -restore */
+
+#endif /* MEMWATCH */
+#endif /* !__MEMWATCH_C */
+
+#ifdef __cplusplus
+ }
+#endif
+
+#if 0 /* 980317: disabled C++ */
+
+/*
+** C++ support section
+** Implements the C++ support. Please note that in order to avoid
+** messing up library classes, C++ support is disabled by default.
+** You must NOT enable it until AFTER the inclusion of all header
+** files belonging to code that are not compiled with MEMWATCH, and
+** possibly for some that are! The reason for this is that a C++
+** class may implement it's own new() function, and the preprocessor
+** would substitute this crucial declaration for MEMWATCH new().
+** You can forcibly deny C++ support by defining MEMWATCH_NOCPP.
+** To enble C++ support, you must be compiling C++, MEMWATCH must
+** be defined, MEMWATCH_NOCPP must not be defined, and finally,
+** you must define 'new' to be 'mwNew', and 'delete' to be 'mwDelete'.
+** Unlike C, C++ code can begin executing *way* before main(), for
+** example if a global variable is created. For this reason, you can
+** declare a global variable of the class 'MemWatch'. If this is
+** is the first variable created, it will then check ALL C++ allocations
+** and deallocations. Unfortunately, this evaluation order is not
+** guaranteed by C++, though the compilers I've tried evaluates them
+** in the order encountered.
+*/
+#ifdef __cplusplus
+#ifndef __MEMWATCH_C
+#ifdef MEMWATCH
+#ifndef MEMWATCH_NOCPP
+extern int mwNCur;
+extern const char *mwNFile;
+extern int mwNLine;
+class MemWatch {
+public:
+ MemWatch();
+ ~MemWatch();
+ };
+void * operator new(size_t);
+void * operator new(size_t,const char *,int);
+void * operator new[] (size_t,const char *,int); // hjc 07/16/02
+void operator delete(void *);
+#define mwNew new(__FILE__,__LINE__)
+#define mwDelete (mwNCur=1,mwNFile=__FILE__,mwNLine=__LINE__),delete
+#endif /* MEMWATCH_NOCPP */
+#endif /* MEMWATCH */
+#endif /* !__MEMWATCH_C */
+#endif /* __cplusplus */
+
+#endif /* 980317: disabled C++ */
+
+#endif /* __MEMWATCH_H */
+
+/* EOF MEMWATCH.H */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c
new file mode 100644
index 0000000000..54bd87f6d9
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c
@@ -0,0 +1,21 @@
+#include <string.h>
+
+#include "strhashfunc.h"
+
+unsigned int hashtable_strhashfunc(void * p)
+{
+ const char * str = (const char *)p;
+ unsigned int hash = 5381;
+ int c;
+
+ while ((c = *str++) != '\0')
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash;
+}
+
+int hashtable_strequalfunc(void * str1, void * str2)
+{
+ return !strcmp(str1, str2);
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h
new file mode 100644
index 0000000000..6f02120a05
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h
@@ -0,0 +1,8 @@
+#ifndef __STRHASHFUNC_H
+#define __STRHASHFUNC_H
+
+unsigned int hashtable_strhashfunc(void *str);
+int hashtable_strequalfunc(void * str1, void * str2);
+
+#endif /* #ifndef __STRHASHFUNC_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/contrib/unicows.dll b/plugins/!NotAdopted/VypressChat/contrib/unicows.dll
new file mode 100644
index 0000000000..d9928088d6
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/unicows.dll
Binary files differ