]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/hashcomp.h
c5d849529c48887d9c51ebe9a9ca8168681bff98
[user/henk/code/inspircd.git] / include / hashcomp.h
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #ifndef _HASHCOMP_H_
15 #define _HASHCOMP_H_
16
17 #include <cstring>
18 #include "hash_map.h"
19
20 /*******************************************************
21  * This file contains classes and templates that deal
22  * with the comparison and hashing of 'irc strings'.
23  * An 'irc string' is a string which compares in a
24  * case insensitive manner, and as per RFC 1459 will
25  * treat [ identical to {, ] identical to }, and \
26  * as identical to |.
27  *
28  * Our hashing functions are designed  to accept
29  * std::string and compare/hash them as type irc::string
30  * by converting them internally. This makes them
31  * backwards compatible with other code which is not
32  * aware of irc::string.
33  *******************************************************/
34
35 #ifndef LOWERMAP
36 #define LOWERMAP
37
38 /** A mapping of uppercase to lowercase, including scandinavian
39  * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \
40  */
41 unsigned const char rfc_case_insensitive_map[256] = {
42         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
43         20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
44         40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
45         60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
46         112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 94, 95, 96, 97, 98, 99,           /* 80-99 */
47         100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
48         120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
49         140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
50         160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
51         180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
52         200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
53         220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
54         240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
55 };
56
57 /** Seperate from the other casemap tables so that code *can* still exclusively rely on RFC casemapping
58  * if it must.
59  *
60  * This is provided as a pointer so that modules can change it to their custom mapping tables,
61  * e.g. for national character support.
62  */
63 CoreExport extern unsigned const char *national_case_insensitive_map;
64
65 /** Case insensitive map, ASCII rules.
66  * That is;
67  * [ != {, but A == a.
68  */
69 unsigned const char ascii_case_insensitive_map[256] = {
70         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
71         20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
72         40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
73         60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
74         112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,              /* 80-99 */
75         100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
76         120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
77         140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
78         160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
79         180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
80         200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
81         220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
82         240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
83 };
84
85 /** Case sensitive map.
86  * Can technically also be used for ASCII case sensitive comparisons, as [ != {, etc.
87  */
88 unsigned const char rfc_case_sensitive_map[256] = {
89         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
90         21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
91         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
92         61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
93         81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
94         101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
95         121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
96         141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
97         161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
98         181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
99         201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
100         221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
101         241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
102 };
103
104 #endif
105
106 template<typename T> const T& SearchAndReplace(T& text, const T& pattern, const T& replace)
107 {
108         T replacement;
109         if ((!pattern.empty()) && (!text.empty()))
110         {
111                 for (std::string::size_type n = 0; n != text.length(); ++n)
112                 {
113                         if (text.length() >= pattern.length() && text.substr(n, pattern.length()) == pattern)
114                         {
115                                 /* Found the pattern in the text, replace it, and advance */
116                                 replacement.append(replace);
117                                 n = n + pattern.length() - 1;
118                         }
119                         else
120                         {
121                                 replacement += text[n];
122                         }
123                 }
124         }
125         text = replacement;
126         return text;
127 }
128
129 /** The irc namespace contains a number of helper classes.
130  */
131 namespace irc
132 {
133
134         /** This class returns true if two strings match.
135          * Case sensitivity is ignored, and the RFC 'character set'
136          * is adhered to
137          */
138         struct StrHashComp
139         {
140                 /** The operator () does the actual comparison in hash_map
141                  */
142                 bool operator()(const std::string& s1, const std::string& s2) const;
143         };
144
145         /** The irc_char_traits class is used for RFC-style comparison of strings.
146          * This class is used to implement irc::string, a case-insensitive, RFC-
147          * comparing string class.
148          */
149         struct irc_char_traits : std::char_traits<char> {
150
151                 /** Check if two chars match.
152                  * @param c1st First character
153                  * @param c2nd Second character
154                  * @return true if the characters are equal
155                  */
156                 static bool eq(char c1st, char c2nd);
157
158                 /** Check if two chars do NOT match.
159                  * @param c1st First character
160                  * @param c2nd Second character
161                  * @return true if the characters are unequal
162                  */
163                 static bool ne(char c1st, char c2nd);
164
165                 /** Check if one char is less than another.
166                  * @param c1st First character
167                  * @param c2nd Second character
168                  * @return true if c1st is less than c2nd
169                  */
170                 static bool lt(char c1st, char c2nd);
171
172                 /** Compare two strings of size n.
173                  * @param str1 First string
174                  * @param str2 Second string
175                  * @param n Length to compare to
176                  * @return similar to strcmp, zero for equal, less than zero for str1
177                  * being less and greater than zero for str1 being greater than str2.
178                  */
179                 static CoreExport int compare(const char* str1, const char* str2, size_t n);
180
181                 /** Find a char within a string up to position n.
182                  * @param s1 String to find in
183                  * @param n Position to search up to
184                  * @param c Character to search for
185                  * @return Pointer to the first occurance of c in s1
186                  */
187                 static CoreExport const char* find(const char* s1, int  n, char c);
188         };
189
190         /** Compose a hex string from raw data.
191          * @param raw The raw data to compose hex from
192          * @pram rawsz The size of the raw data buffer
193          * @return The hex string.
194          */
195         CoreExport std::string hex(const unsigned char *raw, size_t rawsz);
196
197         /** This typedef declares irc::string based upon irc_char_traits.
198          */
199         typedef std::basic_string<char, irc_char_traits, std::allocator<char> > string;
200
201         /** irc::stringjoiner joins string lists into a string, using
202          * the given seperator string.
203          * This class can join a vector of std::string, a deque of
204          * std::string, or a const char* const* array, using overloaded
205          * constructors.
206          */
207         class CoreExport stringjoiner : public classbase
208         {
209          private:
210
211                 /** Output string
212                  */
213                 std::string joined;
214
215          public:
216
217                 /** Join elements of a vector, between (and including) begin and end
218                  * @param seperator The string to seperate values with
219                  * @param sequence One or more items to seperate
220                  * @param begin The starting element in the sequence to be joined
221                  * @param end The ending element in the sequence to be joined
222                  */
223                 stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end);
224
225                 /** Join elements of a deque, between (and including) begin and end
226                  * @param seperator The string to seperate values with
227                  * @param sequence One or more items to seperate
228                  * @param begin The starting element in the sequence to be joined
229                  * @param end The ending element in the sequence to be joined
230                  */
231                 stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end);
232
233                 /** Join elements of an array of char arrays, between (and including) begin and end
234                  * @param seperator The string to seperate values with
235                  * @param sequence One or more items to seperate
236                  * @param begin The starting element in the sequence to be joined
237                  * @param end The ending element in the sequence to be joined
238                  */
239                 stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end);
240
241                 /** Get the joined sequence
242                  * @return A reference to the joined string
243                  */
244                 std::string& GetJoined();
245         };
246
247         /** irc::modestacker stacks mode sequences into a list.
248          * It can then reproduce this list, clamped to a maximum of MAXMODES
249          * values per line.
250          */
251         class CoreExport modestacker : public classbase
252         {
253          private:
254                 InspIRCd* ServerInstance;
255
256                 /** The mode sequence and its parameters
257                  */
258                 std::deque<std::string> sequence;
259
260                 /** True if the mode sequence is initially adding
261                  * characters, false if it is initially removing
262                  * them
263                  */
264                 bool adding;
265          public:
266
267                 /** Construct a new modestacker.
268                  * @param add True if the stack is adding modes,
269                  * false if it is removing them
270                  */
271                 modestacker(InspIRCd* Instance, bool add);
272
273                 /** Push a modeletter and its parameter onto the stack.
274                  * No checking is performed as to if this mode actually
275                  * requires a parameter. If you stack invalid mode
276                  * sequences, they will be tidied if and when they are
277                  * passed to a mode parser.
278                  * @param modeletter The mode letter to insert
279                  * @param parameter The parameter for the mode
280                  */
281                 void Push(char modeletter, const std::string &parameter);
282
283                 /** Push a modeletter without parameter onto the stack.
284                  * No checking is performed as to if this mode actually
285                  * requires a parameter. If you stack invalid mode
286                  * sequences, they will be tidied if and when they are
287                  * passed to a mode parser.
288                  * @param modeletter The mode letter to insert
289                  */
290                 void Push(char modeletter);
291
292                 /** Push a '+' symbol onto the stack.
293                  */
294                 void PushPlus();
295
296                 /** Push a '-' symbol onto the stack.
297                  */
298                 void PushMinus();
299
300                 /** Return zero or more elements which form the
301                  * mode line. This will be clamped to a max of
302                  * MAXMODES items (MAXMODES-1 mode parameters and
303                  * one mode sequence string), and max_line_size
304                  * characters. As specified below, this function
305                  * should be called in a loop until it returns zero,
306                  * indicating there are no more modes to return.
307                  * @param result The vector to populate. This will not
308                  * be cleared before it is used.
309                  * @param max_line_size The maximum size of the line
310                  * to build, in characters, seperate to MAXMODES.
311                  * @return The number of elements in the deque.
312                  * The function should be called repeatedly until it
313                  * returns 0, in case there are multiple lines of
314                  * mode changes to be obtained.
315                  */
316                 int GetStackedLine(std::vector<std::string> &result, int max_line_size = 360);
317
318                 /** deprecated compatability interface - TODO remove */
319                 int GetStackedLine(std::deque<std::string> &result, int max_line_size = 360) {
320                         std::vector<std::string> r;
321                         int n = GetStackedLine(r, max_line_size);
322                         result.clear();
323                         result.insert(result.end(), r.begin(), r.end());
324                         return n;
325                 }
326         };
327
328         /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812.
329          * It will split the string into 'tokens' each containing one parameter
330          * from the string.
331          * For instance, if it is instantiated with the string:
332          * "PRIVMSG #test :foo bar baz qux"
333          * then each successive call to tokenstream::GetToken() will return
334          * "PRIVMSG", "#test", "foo bar baz qux", "".
335          * Note that if the whole string starts with a colon this is not taken
336          * to mean the string is all one parameter, and the first item in the
337          * list will be ":item". This is to allow for parsing 'source' fields
338          * from data.
339          */
340         class CoreExport tokenstream : public classbase
341         {
342          private:
343
344                 /** Original string
345                  */
346                 std::string tokens;
347
348                 /** Last position of a seperator token
349                  */
350                 std::string::iterator last_starting_position;
351
352                 /** Current string position
353                  */
354                 std::string::iterator n;
355
356                 /** True if the last value was an ending value
357                  */
358                 bool last_pushed;
359          public:
360
361                 /** Create a tokenstream and fill it with the provided data
362                  */
363                 tokenstream(const std::string &source);
364
365                 /** Destructor
366                  */
367                 ~tokenstream();
368
369                 /** Fetch the next token from the stream as a std::string
370                  * @param token The next token available, or an empty string if none remain
371                  * @return True if tokens are left to be read, false if the last token was just retrieved.
372                  */
373                 bool GetToken(std::string &token);
374
375                 /** Fetch the next token from the stream as an irc::string
376                  * @param token The next token available, or an empty string if none remain
377                  * @return True if tokens are left to be read, false if the last token was just retrieved.
378                  */
379                 bool GetToken(irc::string &token);
380
381                 /** Fetch the next token from the stream as an integer
382                  * @param token The next token available, or undefined if none remain
383                  * @return True if tokens are left to be read, false if the last token was just retrieved.
384                  */
385                 bool GetToken(int &token);
386
387                 /** Fetch the next token from the stream as a long integer
388                  * @param token The next token available, or undefined if none remain
389                  * @return True if tokens are left to be read, false if the last token was just retrieved.
390                  */
391                 bool GetToken(long &token);
392         };
393
394         /** irc::sepstream allows for splitting token seperated lists.
395          * Each successive call to sepstream::GetToken() returns
396          * the next token, until none remain, at which point the method returns
397          * an empty string.
398          */
399         class CoreExport sepstream : public classbase
400         {
401          private:
402                 /** Original string.
403                  */
404                 std::string tokens;
405                 /** Last position of a seperator token
406                  */
407                 std::string::iterator last_starting_position;
408                 /** Current string position
409                  */
410                 std::string::iterator n;
411                 /** Seperator value
412                  */
413                 char sep;
414          public:
415                 /** Create a sepstream and fill it with the provided data
416                  */
417                 sepstream(const std::string &source, char seperator);
418
419                 /** Destructor
420                  */
421                 virtual ~sepstream();
422
423                 /** Fetch the next token from the stream
424                  * @param token The next token from the stream is placed here
425                  * @return True if tokens still remain, false if there are none left
426                  */
427                 virtual bool GetToken(std::string &token);
428
429                 /** Fetch the entire remaining stream, without tokenizing
430                  * @return The remaining part of the stream
431                  */
432                 virtual const std::string GetRemaining();
433
434                 /** Returns true if the end of the stream has been reached
435                  * @return True if the end of the stream has been reached, otherwise false
436                  */
437                 virtual bool StreamEnd();
438         };
439
440         /** A derived form of sepstream, which seperates on commas
441          */
442         class CoreExport commasepstream : public sepstream
443         {
444          public:
445                 /** Initialize with comma seperator
446                  */
447                 commasepstream(const std::string &source) : sepstream(source, ',')
448                 {
449                 }
450         };
451
452         /** A derived form of sepstream, which seperates on spaces
453          */
454         class CoreExport spacesepstream : public sepstream
455         {
456          public:
457                 /** Initialize with space seperator
458                  */
459                 spacesepstream(const std::string &source) : sepstream(source, ' ')
460                 {
461                 }
462         };
463
464         /** The portparser class seperates out a port range into integers.
465          * A port range may be specified in the input string in the form
466          * "6660,6661,6662-6669,7020". The end of the stream is indicated by
467          * a return value of 0 from portparser::GetToken(). If you attempt
468          * to specify an illegal range (e.g. one where start >= end, or
469          * start or end < 0) then GetToken() will return the first element
470          * of the pair of numbers.
471          */
472         class CoreExport portparser : public classbase
473         {
474          private:
475
476                 /** Used to split on commas
477                  */
478                 commasepstream* sep;
479
480                 /** Current position in a range of ports
481                  */
482                 long in_range;
483
484                 /** Starting port in a range of ports
485                  */
486                 long range_begin;
487
488                 /** Ending port in a range of ports
489                  */
490                 long range_end;
491
492                 /** Allow overlapped port ranges
493                  */
494                 bool overlapped;
495
496                 /** Used to determine overlapping of ports
497                  * without O(n) algorithm being used
498                  */
499                 std::map<long, bool> overlap_set;
500
501                 /** Returns true if val overlaps an existing range
502                  */
503                 bool Overlaps(long val);
504          public:
505
506                 /** Create a portparser and fill it with the provided data
507                  * @param source The source text to parse from
508                  * @param allow_overlapped Allow overlapped ranges
509                  */
510                 portparser(const std::string &source, bool allow_overlapped = true);
511
512                 /** Frees the internal commasepstream object
513                  */
514                 ~portparser();
515
516                 /** Fetch the next token from the stream
517                  * @return The next port number is returned, or 0 if none remain
518                  */
519                 long GetToken();
520         };
521
522         /** Turn _ characters in a string into spaces
523          * @param n String to translate
524          * @return The new value with _ translated to space.
525          */
526         CoreExport const char* Spacify(const char* n);
527 }
528
529 /* Define operators for using >> and << with irc::string to an ostream on an istream. */
530 /* This was endless fun. No. Really. */
531 /* It was also the first core change Ommeh made, if anyone cares */
532
533 /** Operator << for irc::string
534  */
535 inline std::ostream& operator<<(std::ostream &os, const irc::string &str) { return os << str.c_str(); }
536
537 /** Operator >> for irc::string
538  */
539 inline std::istream& operator>>(std::istream &is, irc::string &str)
540 {
541         std::string tmp;
542         is >> tmp;
543         str = tmp.c_str();
544         return is;
545 }
546
547 /* Define operators for + and == with irc::string to std::string for easy assignment
548  * and comparison
549  *
550  * Operator +
551  */
552 inline std::string operator+ (std::string& leftval, irc::string& rightval)
553 {
554         return leftval + std::string(rightval.c_str());
555 }
556
557 /* Define operators for + and == with irc::string to std::string for easy assignment
558  * and comparison
559  *
560  * Operator +
561  */
562 inline irc::string operator+ (irc::string& leftval, std::string& rightval)
563 {
564         return leftval + irc::string(rightval.c_str());
565 }
566
567 /* Define operators for + and == with irc::string to std::string for easy assignment
568  * and comparison
569  *
570  * Operator ==
571  */
572 inline bool operator== (const std::string& leftval, const irc::string& rightval)
573 {
574         return (leftval.c_str() == rightval);
575 }
576
577 /* Define operators for + and == with irc::string to std::string for easy assignment
578  * and comparison
579  *
580  * Operator ==
581  */
582 inline bool operator== (const irc::string& leftval, const std::string& rightval)
583 {
584         return (leftval == rightval.c_str());
585 }
586
587 /* Define operators != for irc::string to std::string for easy comparison
588  */
589 inline bool operator!= (const irc::string& leftval, const std::string& rightval)
590 {
591         return !(leftval == rightval.c_str());
592 }
593
594 /* Define operators != for std::string to irc::string for easy comparison
595  */
596 inline bool operator!= (const std::string& leftval, const irc::string& rightval)
597 {
598         return !(leftval.c_str() == rightval);
599 }
600
601 // FIXME MAXBUF messes up these
602 #if 0
603 template<std::size_t N>
604 static inline bool operator == (std::string const &lhs, char const (&rhs)[N])
605 {
606         return lhs.length() == N - 1 && !std::memcmp(lhs.data(), rhs, N - 1);
607 }
608
609 template<std::size_t N>
610 static inline bool operator != (std::string const &lhs, char const (&rhs)[N])
611 {
612         return !(lhs == rhs);
613 }
614 #endif
615
616 /** Assign an irc::string to a std::string.
617  */
618 inline std::string assign(const irc::string &other) { return other.c_str(); }
619
620 /** Assign a std::string to an irc::string.
621  */
622 inline irc::string assign(const std::string &other) { return other.c_str(); }
623
624 /** Trim the leading and trailing spaces from a std::string.
625  */
626 inline std::string& trim(std::string &str)
627 {
628         std::string::size_type start = str.find_first_not_of(" ");
629         std::string::size_type end = str.find_last_not_of(" ");
630         if (start == std::string::npos || end == std::string::npos)
631                 str = "";
632         else
633                 str = str.substr(start, end-start+1);
634
635         return str;
636 }
637
638 /** Hashing stuff is totally different on vc++'s hash_map implementation, so to save a buttload of
639  * #ifdefs we'll just do it all at once. Except, of course, with TR1, when it's the same as GCC.
640  */
641 BEGIN_HASHMAP_NAMESPACE
642
643         /** Hashing function to hash irc::string
644          */
645 #if defined(WINDOWS) && !defined(HAS_TR1_UNORDERED)
646         template<> class CoreExport hash_compare<irc::string, std::less<irc::string> >
647         {
648         public:
649                 enum { bucket_size = 4, min_buckets = 8 }; /* Got these numbers from the CRT source, if anyone wants to change them feel free. */
650
651                 /** Compare two irc::string values for hashing in hash_map
652                  */
653                 bool operator()(const irc::string & s1, const irc::string & s2) const
654                 {
655                         if(s1.length() != s2.length()) return true;
656                         return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0);
657                 }
658
659                 /** Hash an irc::string value for hash_map
660                  */
661                 size_t operator()(const irc::string & s) const;
662         };
663
664         template<> class CoreExport hash_compare<std::string, std::less<std::string> >
665         {
666         public:
667                 enum { bucket_size = 4, min_buckets = 8 }; /* Again, from the CRT source */
668
669                 /** Compare two std::string values for hashing in hash_map
670                  */
671                 bool operator()(const std::string & s1, const std::string & s2) const
672                 {
673                         if(s1.length() != s2.length()) return true;
674                         return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0);
675                 }
676
677                 /** Hash a std::string using RFC1459 case sensitivity rules
678                 * @param s A string to hash
679                 * @return The hash value
680                 */
681                 size_t operator()(const std::string & s) const;
682         };
683 #else
684
685         template<> struct hash<irc::string>
686         {
687                 /** Hash an irc::string using RFC1459 case sensitivity rules
688                  * @param s A string to hash
689                  * @return The hash value
690                  */
691                 size_t CoreExport operator()(const irc::string &s) const;
692         };
693
694         /* XXX FIXME: Implement a hash function overriding std::string's that works with TR1! */
695
696 #ifdef HASHMAP_DEPRECATED
697         struct insensitive
698 #else
699         CoreExport template<> struct hash<std::string>
700 #endif
701         {
702                 size_t CoreExport operator()(const std::string &s) const;
703         };
704
705 #endif
706
707         /** Convert a string to lower case respecting RFC1459
708         * @param n A string to lowercase
709         */
710         void strlower(char *n);
711
712 END_HASHMAP_NAMESPACE
713
714 #endif