summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2012-12-27 21:23:46 -0800
committerBen Longbons <b.r.longbons@gmail.com>2013-01-07 15:31:38 -0800
commitc080e504e4d74027b985b1ed675c172c083cea76 (patch)
treeac084d16d9d40c0a0c950b66eb62a0e16795d486 /src/common
parentae30173d71d3bfc8514dbe70b6c90c9a3324b8fc (diff)
downloadtmwa-c080e504e4d74027b985b1ed675c172c083cea76.tar.gz
tmwa-c080e504e4d74027b985b1ed675c172c083cea76.tar.bz2
tmwa-c080e504e4d74027b985b1ed675c172c083cea76.tar.xz
tmwa-c080e504e4d74027b985b1ed675c172c083cea76.zip
Use cxxstdio
Diffstat (limited to 'src/common')
-rw-r--r--src/common/const_array.hpp175
-rw-r--r--src/common/cxxstdio.hpp123
-rw-r--r--src/common/db.cpp4
-rw-r--r--src/common/extract.hpp208
-rw-r--r--src/common/grfio.cpp11
-rw-r--r--src/common/lock.cpp17
-rw-r--r--src/common/mmo.hpp15
-rw-r--r--src/common/nullpo.cpp3
-rw-r--r--src/common/nullpo.hpp1
-rw-r--r--src/common/sanity.hpp23
-rw-r--r--src/common/socket.cpp15
-rw-r--r--src/common/timer.cpp5
-rw-r--r--src/common/utils.cpp58
-rw-r--r--src/common/utils.hpp26
14 files changed, 624 insertions, 60 deletions
diff --git a/src/common/const_array.hpp b/src/common/const_array.hpp
new file mode 100644
index 0000000..12c5d6f
--- /dev/null
+++ b/src/common/const_array.hpp
@@ -0,0 +1,175 @@
+#ifndef CONST_ARRAY_HPP
+#define CONST_ARRAY_HPP
+// const_array.hpp - just a pointer-to-const and a length
+//
+// Copyright © 2011-2012 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>.
+
+#include "sanity.hpp"
+
+#include <cstring>
+
+#include <iterator>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#ifdef ANNOYING_GCC46_WORKAROUNDS
+# warning "like this one"
+// constexpr is buggy with templates in this version
+# define constexpr /* nothing */
+#endif
+
+template<class T>
+class const_array
+{
+ const T *d;
+ size_t n;
+public:
+ typedef const T *iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+
+ constexpr
+ const_array(std::nullptr_t)
+ : d(nullptr), n(0)
+ {}
+
+ constexpr
+ const_array(const T *p, size_t z)
+ : d(p), n(z)
+ {}
+
+ constexpr
+ const_array(const T *b, const T *e)
+ : d(b), n(e - b)
+ {}
+
+ const_array(std::initializer_list<T> list)
+ : d(list.begin()), n(list.size())
+ {}
+
+ // Implicit conversion from std::vector
+ const_array(const std::vector<T>& v)
+ : d(v.data()), n(v.size())
+ {}
+
+ // but disallow conversion from a temporary
+ const_array(std::vector<T>&&) = delete;
+
+ // All methods are non-const to "encourage" you
+ // to always pass a const_array by value.
+ // After all, "const const_array" looks funny.
+ constexpr
+ const T *data() { return d; }
+ constexpr
+ size_t size() { return n; }
+ constexpr
+ bool empty() { return not n; }
+ constexpr explicit
+ operator bool() { return n; }
+
+ constexpr
+ std::pair<const_array, const_array> cut(size_t o)
+ {
+ return {const_array(d, o), const_array(d + o, n - o)};
+ }
+
+ constexpr
+ const_array first(size_t o)
+ {
+ return cut(o).first;
+ }
+
+ constexpr
+ const_array last(size_t l)
+ {
+ return cut(size() - l).second;
+ }
+
+ constexpr
+ const_array after(size_t o)
+ {
+ return cut(o).second;
+ }
+
+ constexpr
+ iterator begin() { return d; }
+ constexpr
+ iterator end() { return d + n; }
+ constexpr
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ constexpr
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+
+ constexpr
+ const T& front() { return *begin(); }
+ constexpr
+ const T& back() { return *rbegin(); }
+};
+
+// subclass just provides a simpler name and some conversions
+class const_string : public const_array<char>
+{
+public:
+ // Implicit conversion from C string.
+ constexpr
+ const_string(const char *z)
+ : const_array<char>(z, z ? strlen(z) : 0)
+ {}
+
+ // Same as parent constructor.
+ constexpr
+ const_string(const char *s, size_t l)
+ : const_array<char>(s, l)
+ {}
+
+ // Same as parent constructor.
+ constexpr
+ const_string(const char *b, const char *e)
+ : const_array<char>(b, e)
+ {}
+
+ // Same as parent constructor.
+ const_string(const std::vector<char> s)
+ : const_array<char>(s)
+ {}
+
+ // Implicit conversion from C++ string.
+ const_string(const std::string& s)
+ : const_array<char>(s.data(), s.size())
+ {}
+
+ // but disallow converion from a temporary.
+ const_string(std::string&&) = delete;
+
+ // allow being sloppy
+ constexpr
+ const_string(const_array<char> a)
+ : const_array<char>(a)
+ {}
+};
+#ifdef ANNOYING_GCC46_WORKAROUNDS
+# undef constexpr
+#endif
+
+inline
+std::ostream& operator << (std::ostream& o, const_string s)
+{
+ return o.write(s.data(), s.size());
+}
+
+#endif // CONST_ARRAY_HPP
diff --git a/src/common/cxxstdio.hpp b/src/common/cxxstdio.hpp
index 2bd4f4b..6f5072e 100644
--- a/src/common/cxxstdio.hpp
+++ b/src/common/cxxstdio.hpp
@@ -27,6 +27,8 @@
#include <string>
+#include "const_array.hpp"
+
namespace cxxstdio
{
inline __attribute__((format(printf, 2, 0)))
@@ -58,16 +60,20 @@ namespace cxxstdio
return vfscanf(in, fmt, ap);
}
+#if 0
inline __attribute__((format(scanf, 2, 0)))
int do_vscan(const char *in, const char *fmt, va_list ap)
{
return vsscanf(in, fmt, ap);
}
+#else
+ int do_vscan(const char *in, const char *fmt, va_list ap) = delete;
+#endif
inline __attribute__((format(scanf, 2, 0)))
int do_vscan(const std::string& in, const char *fmt, va_list ap)
{
- return do_vscan(in.c_str(), fmt, ap);
+ return vsscanf(in.c_str(), fmt, ap);
}
@@ -109,11 +115,63 @@ namespace cxxstdio
return v;
}
+#if 0
+ template<class E>
+ constexpr
+ E get_enum_min_value(decltype(E::min_value))
+ {
+ return E::min_value;
+ }
+ template<class E>
+ constexpr
+ E get_enum_min_value(E def)
+ {
+ return def;
+ }
+
+ template<class E>
+ constexpr
+ E get_enum_max_value(decltype(E::max_value))
+ {
+ return E::max_value;
+ }
+ template<class E>
+ constexpr
+ E get_enum_max_value(E def)
+ {
+ return def;
+ }
+#else
+ template<class E>
+ constexpr
+ E get_enum_min_value(E)
+ {
+ return E::min_value;
+ }
+ template<class E>
+ constexpr
+ E get_max_value(E)
+ {
+ return E::max_value;
+ }
+#endif
+
template<class E>
class EnumConverter
{
E& out;
typedef typename underlying_type<E>::type U;
+#if 0
+ constexpr static
+ U min_value = U(get_enum_min_value<E>(E(std::numeric_limits<U>::min())));
+ constexpr static
+ U max_value = U(get_enum_max_value<E>(E(std::numeric_limits<U>::max())));
+#else
+ constexpr static
+ U min_value = U(get_enum_min_value(E()));
+ constexpr static
+ U max_value = U(get_enum_max_value(E()));
+#endif
U mid;
public:
EnumConverter(E& e)
@@ -121,7 +179,10 @@ namespace cxxstdio
{}
~EnumConverter()
{
- if (U(E::min_value) <= mid && mid <= U(E::max_value))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wtype-limits"
+ if (min_value <= mid && mid <= max_value)
+#pragma GCC diagnostic pop
out = E(mid);
}
U *operator &()
@@ -201,30 +262,40 @@ namespace cxxstdio
}
};
-#define FPRINTF(file, fmt, args...) \
- ({ \
- struct format_impl \
- { \
- constexpr static \
- const char *print_format() { return fmt; } \
- }; \
- cxxstdio::PrintFormatter<format_impl>::print(file, args); \
- })
-
-#define FSCANF(file, fmt, args...) \
- ({ \
- struct format_impl \
- { \
- constexpr static \
- const char *scan_format() { return fmt; } \
- }; \
- cxxstdio::ScanFormatter<format_impl>::scan(file, args); \
- })
-
-#define PRINTF(fmt, args...) FPRINTF(stdout, fmt, args)
-#define STRPRINTF(str, fmt, args...) FPRINTF(str, fmt, args)
-#define SCANF(fmt, args...) FSCANF(stdin, fmt, args)
-#define SSCANF(str, fmt, args...) FSCANF(str, fmt, args)
+#define FPRINTF(file, fmt, args...) \
+ ([&]() -> int \
+ { \
+ struct format_impl \
+ { \
+ constexpr static \
+ const char *print_format() { return fmt; } \
+ }; \
+ return cxxstdio::PrintFormatter<format_impl>::print(file, ## args);\
+ }())
+
+#define FSCANF(file, fmt, args...) \
+ ([&]() -> int \
+ { \
+ struct format_impl \
+ { \
+ constexpr static \
+ const char *scan_format() { return fmt; } \
+ }; \
+ return cxxstdio::ScanFormatter<format_impl>::scan(file, ## args); \
+ }())
+
+#define PRINTF(fmt, args...) FPRINTF(stdout, fmt, ## args)
+#define SPRINTF(str, fmt, args...) FPRINTF(str, fmt, ## args)
+#define SCANF(fmt, args...) FSCANF(stdin, fmt, ## args)
+#define SSCANF(str, fmt, args...) FSCANF(str, fmt, ## args)
+
+#define STRPRINTF(fmt, args...) \
+ ([&]() -> std::string \
+ { \
+ std::string _out_impl; \
+ SPRINTF(_out_impl, fmt, ## args); \
+ return _out_impl; \
+ }())
} // namespace cxxstdio
diff --git a/src/common/db.cpp b/src/common/db.cpp
index 8780138..06f85bc 100644
--- a/src/common/db.cpp
+++ b/src/common/db.cpp
@@ -414,12 +414,12 @@ void db_walk_tree(bool dealloc, struct dbn* p, db_func_t func)
return;
if (!dealloc && !func)
{
- fprintf(stderr, "DEBUG: Must walk tree to either free or invoke a function.\n");
+ FPRINTF(stderr, "DEBUG: Must walk tree to either free or invoke a function.\n");
abort();
}
if (p->parent)
{
- fprintf(stderr, "DEBUG: Root nodes must not have parents\n");
+ FPRINTF(stderr, "DEBUG: Root nodes must not have parents\n");
abort();
}
while (true)
diff --git a/src/common/extract.hpp b/src/common/extract.hpp
new file mode 100644
index 0000000..52df9df
--- /dev/null
+++ b/src/common/extract.hpp
@@ -0,0 +1,208 @@
+#ifndef EXTRACT_HPP
+#define EXTRACT_HPP
+// extract.hpp - a simple, heirarchail, tokenizer
+//
+// Copyright © 2012 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>.
+
+#include <algorithm>
+
+#include "const_array.hpp"
+#include "mmo.hpp"
+
+template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value>::type>
+bool extract(const_string str, T *iv)
+{
+ if (!str || str.size() > 20)
+ return false;
+ switch (str.front())
+ {
+ case '-':
+ case '0' ... '9':
+ break;
+ default:
+ return false;
+ }
+ char buf[20 + 1];
+ std::copy(str.begin(), str.end(), buf);
+ buf[str.size()] = '\0';
+
+ char *end;
+ errno = 0;
+ if (std::is_signed<T>::value)
+ {
+ long long v = strtoll(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+ else
+ {
+ if (str.front() == '-')
+ return false;
+ unsigned long long v = strtoull(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+}
+
+// extra typename=void to workaround some duplicate overload rule
+template<class T, typename=typename std::enable_if<std::is_enum<T>::value>::type, typename=void>
+bool extract(const_string str, T *iv)
+{
+ typedef typename underlying_type<T>::type U;
+ U v;
+ // defer to integer version
+ if (!extract(str, &v))
+ return false;
+ // TODO check bounds ...
+ *iv = static_cast<T>(v);
+ return true;
+}
+
+inline
+bool extract(const_string str, const_string *rv)
+{
+ *rv = str;
+ return true;
+}
+
+inline
+bool extract(const_string str, std::string *rv)
+{
+ *rv = std::string(str.begin(), str.end());
+ return true;
+}
+
+template<size_t N>
+__attribute__((deprecated))
+bool extract(const_string str, char (*out)[N])
+{
+ if (str.size() >= N)
+ return false;
+ std::copy(str.begin(), str.end(), *out);
+ std::fill(*out + str.size() , *out + N, '\0');
+ return true;
+}
+
+// basically just a std::tuple
+// but it provides its data members publically
+template<char split, class... T>
+class Record;
+template<char split>
+class Record<split>
+{
+};
+template<char split, class F, class... R>
+class Record<split, F, R...>
+{
+public:
+ F frist;
+ Record<split, R...> rest;
+public:
+ Record(F f, R... r)
+ : frist(f), rest(r...)
+ {}
+};
+template<char split, class... T>
+Record<split, T...> record(T... t)
+{
+ return Record<split, T...>(t...);
+}
+
+template<char split>
+bool extract(const_string str, Record<split>)
+{
+ return !str;
+}
+template<char split, class F, class... R>
+bool extract(const_string str, Record<split, F, R...> rec)
+{
+ const char *s = std::find(str.begin(), str.end(), split);
+ if (s == str.end())
+ return sizeof...(R) == 0 && extract(str, rec.frist);
+ return extract(const_string(str.begin(), s), rec.frist)
+ && extract(const_string(s + 1, str.end()), rec.rest);
+}
+
+template<char split, class T>
+struct VRecord
+{
+ std::vector<T> *arr;
+};
+
+template<char split, class T>
+VRecord<split, T> vrec(std::vector<T> *arr)
+{
+ return {arr};
+}
+
+template<char split, class T>
+bool extract(const_string str, VRecord<split, T> rec)
+{
+ if (str.empty())
+ return true;
+ const char *s = std::find(str.begin(), str.end(), split);
+ rec.arr->emplace_back();
+ if (s == str.end())
+ return extract(str, &rec.arr->back());
+ return extract(const_string(str.begin(), s), &rec.arr->back())
+ && extract(const_string(s + 1, str.end()), rec);
+}
+
+inline
+bool extract(const_string str, struct global_reg *var)
+{
+ return extract(str,
+ record<','>(&var->str, &var->value));
+}
+
+inline
+bool extract(const_string str, struct item *it)
+{
+ return extract(str,
+ record<','>(
+ &it->id,
+ &it->nameid,
+ &it->amount,
+ &it->equip,
+ &it->identify,
+ &it->refine,
+ &it->attribute,
+ &it->card[0],
+ &it->card[1],
+ &it->card[2],
+ &it->card[3],
+ &it->broken))
+ || extract(str,
+ record<','>(
+ &it->id,
+ &it->nameid,
+ &it->amount,
+ &it->equip,
+ &it->identify,
+ &it->refine,
+ &it->attribute,
+ &it->card[0],
+ &it->card[1],
+ &it->card[2],
+ &it->card[3]));
+}
+#endif // EXTRACT_HPP
diff --git a/src/common/grfio.cpp b/src/common/grfio.cpp
index 7eb847a..93ea4ca 100644
--- a/src/common/grfio.cpp
+++ b/src/common/grfio.cpp
@@ -7,6 +7,7 @@
#include <cstdlib>
#include <cstring>
+#include "cxxstdio.hpp"
#include "mmo.hpp"
#include "socket.hpp"
#include "utils.hpp"
@@ -73,7 +74,7 @@ FILELIST *filelist_add(FILELIST * entry)
{
if (filelist_entrys >= FILELIST_LIMIT)
{
- fprintf(stderr, "filelist limit : filelist_add\n");
+ FPRINTF(stderr, "filelist limit : filelist_add\n");
exit(1);
}
@@ -118,7 +119,7 @@ void grfio_resnametable(const char *fname, char *lfname)
FILE *fp = fopen_(restable, "rb");
if (fp == NULL)
{
- fprintf(stderr, "No resnametable, can't look for %s\n", fname);
+ FPRINTF(stderr, "No resnametable, can't look for %s\n", fname);
strcpy(lfname, fname);
char* ext = lfname + strlen(lfname) - 4;
if (!strcmp(ext, ".gat"))
@@ -142,7 +143,7 @@ void grfio_resnametable(const char *fname, char *lfname)
return;
}
}
- fprintf(stderr, "Unable to find resource: %s\n", fname);
+ FPRINTF(stderr, "Unable to find resource: %s\n", fname);
fclose_(fp);
strcpy(lfname, fname);
@@ -177,7 +178,7 @@ size_t grfio_size(const char *fname)
}
else
{
- printf("%s not found\n", fname);
+ PRINTF("%s not found\n", fname);
return 0;
}
return entry->declen;
@@ -195,7 +196,7 @@ void *grfio_reads(const char *fname, size_t *size)
FILE *in = fopen_(lfname, "rb");
if (!in)
{
- fprintf(stderr, "%s not found\n", fname);
+ FPRINTF(stderr, "%s not found\n", fname);
return NULL;
}
FILELIST lentry;
diff --git a/src/common/lock.cpp b/src/common/lock.cpp
index 9075cbb..08ec2c4 100644
--- a/src/common/lock.cpp
+++ b/src/common/lock.cpp
@@ -4,6 +4,7 @@
#include <cstdio>
+#include "cxxstdio.hpp"
#include "socket.hpp"
/// Protected file writing
@@ -12,28 +13,28 @@
// Start writing a tmpfile
FILE *lock_fopen(const char *filename, int *info)
{
- char newfile[512];
FILE *fp;
int no = getpid();
// Get a filename that doesn't already exist
+ std::string newfile;
do
{
- sprintf(newfile, "%s_%d.tmp", filename, no++);
+ newfile = STRPRINTF("%s_%d.tmp", filename, no++);
+ fp = fopen_(newfile.c_str(), "wx");
}
- while ((fp = fopen_(newfile, "r")) && (fclose_(fp), 1));
+ while (!fp);
*info = --no;
- return fopen_(newfile, "w");
+ return fp;
}
// Delete the old file and rename the new file
-void lock_fclose(FILE * fp, const char *filename, int *info)
+void lock_fclose(FILE *fp, const char *filename, int *info)
{
- char newfile[512];
if (fp)
{
fclose_(fp);
- sprintf(newfile, "%s_%d.tmp", filename, *info);
- rename(newfile, filename);
+ std::string newfile = STRPRINTF("%s_%d.tmp", filename, *info);
+ rename(newfile.c_str(), filename);
}
}
diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp
index cda097a..197816f 100644
--- a/src/common/mmo.hpp
+++ b/src/common/mmo.hpp
@@ -21,6 +21,10 @@
# define MAX_CART 100
enum class SkillID : uint16_t;
constexpr SkillID MAX_SKILL = SkillID(474); // not 450
+constexpr SkillID get_enum_min_value(SkillID) { return SkillID(); }
+constexpr SkillID get_enum_max_value(SkillID) { return MAX_SKILL; }
+
+
# define GLOBAL_REG_NUM 96
# define ACCOUNT_REG_NUM 16
# define ACCOUNT_REG2_NUM 16
@@ -71,6 +75,9 @@ enum class EPOS : uint16_t
ARROW = 0x8000,
};
ENUM_BITWISE_OPERATORS(EPOS)
+
+constexpr EPOS get_enum_min_value(EPOS) { return EPOS(0x0000); }
+constexpr EPOS get_enum_max_value(EPOS) { return EPOS(0xffff); }
}
using e::EPOS;
@@ -80,9 +87,9 @@ struct item
short nameid;
short amount;
EPOS equip;
- char identify;
- char refine;
- char attribute;
+ uint8_t identify;
+ uint8_t refine;
+ uint8_t attribute;
short card[4];
short broken;
};
@@ -116,6 +123,8 @@ struct global_reg
namespace e
{
enum class Option : uint16_t;
+constexpr Option get_enum_min_value(Option) { return Option(0x0000); }
+constexpr Option get_enum_max_value(Option) { return Option(0xffff); }
}
using e::Option;
diff --git a/src/common/nullpo.cpp b/src/common/nullpo.cpp
index 8aa2e6e..f5d75cc 100644
--- a/src/common/nullpo.cpp
+++ b/src/common/nullpo.cpp
@@ -11,7 +11,8 @@ void nullpo_info(const char *file, int line, const char *func)
if (!func || !*func)
func = "unknown";
- fprintf(stderr, "%s:%d: in func `%s': NULL pointer\n", file, line, func);
+ fprintf(stderr, "%s:%d: in func `%s': NULL pointer\n",
+ file, line, func);
}
bool nullpo_chk(const char *file, int line, const char *func,
diff --git a/src/common/nullpo.hpp b/src/common/nullpo.hpp
index b729d94..7d12033 100644
--- a/src/common/nullpo.hpp
+++ b/src/common/nullpo.hpp
@@ -8,7 +8,6 @@
/// nullpo_ret(cond) - return 0 if given pointer is NULL
/// nullpo_retv(cond) - just return (function returns void)
/// nullpo_retr(rv, cond) - return given value instead
-/// the _f variants take a printf-format string and arguments
# ifdef NULLPO_CHECK
# define NLP_MARK __FILE__, __LINE__, __func__
diff --git a/src/common/sanity.hpp b/src/common/sanity.hpp
index c4f75e0..9bad464 100644
--- a/src/common/sanity.hpp
+++ b/src/common/sanity.hpp
@@ -1,20 +1,35 @@
/// return wrappers for unexpected NULL pointers
#ifndef SANITY_HPP
#define SANITY_HPP
+
# ifndef __cplusplus
# error "Please compile in C++ mode"
# endif
-# if __GNUC__ < 3
-// I don't specifically know what version this requires,
-// but GCC 3 was the beginning of modern GCC
-# error "Please upgrade your compiler to at least GCC 3"
+
+# if __GNUC__ < 4
+# error "Your compiler is absolutely ancient. You have no chance ..."
+# endif
+
+# if __GNUC__ == 4
+// clang identifies as GCC 4.2, but is mostly okay.
+// Until a bug-free release of it happens, though, I won't recommend it.
+// (patched) clang 3.1 would be the requirement
+# if __GNUC_MINOR__ < 6 && !defined(__clang__)
+# error "Please upgrade to at least GCC 4.6"
+# endif
+# if __GNUC_MINOR__ == 6
+# warning "Working around some annoying bugs in GCC 4.6 ..."
+# define ANNOYING_GCC46_WORKAROUNDS
+# endif
# endif
+
# ifndef __i386__
// Known platform dependencies:
// endianness for the [RW]FIFO.* macros
// possibly, some signal-handling
# error "Unsupported platform"
# endif
+
# ifdef __x86_64__
// I'm working on it - I know there are some pointer-size assumptions.
# error "Sorry, this code is believed not to be 64-bit safe"
diff --git a/src/common/socket.cpp b/src/common/socket.cpp
index f223b83..d3485d3 100644
--- a/src/common/socket.cpp
+++ b/src/common/socket.cpp
@@ -14,6 +14,7 @@
#include <cstdlib>
#include <cstring>
+#include "cxxstdio.hpp"
#include "mmo.hpp"
#include "utils.hpp"
@@ -86,7 +87,7 @@ void send_from_fifo(int fd)
static
void null_parse(int fd)
{
- printf("null_parse : %d\n", fd);
+ PRINTF("null_parse : %d\n", fd);
RFIFOSKIP(fd, RFIFOREST(fd));
}
@@ -109,7 +110,7 @@ void connect_client(int listen_fd)
}
if (!free_fds())
{
- fprintf(stderr, "softlimit reached, disconnecting : %d\n", fd);
+ FPRINTF(stderr, "softlimit reached, disconnecting : %d\n", fd);
delete_session(fd);
return;
}
@@ -275,7 +276,7 @@ void delete_session(int fd)
currentuse--;
if (currentuse < 0)
{
- fprintf(stderr, "delete_session: current sessions negative!\n");
+ FPRINTF(stderr, "delete_session: current sessions negative!\n");
currentuse = 0;
}
return;
@@ -302,12 +303,12 @@ void WFIFOSET(int fd, size_t len)
if (s->wdata_size + len + 16384 > s->max_wdata)
{
realloc_fifo(fd, s->max_rdata, s->max_wdata << 1);
- printf("socket: %d wdata expanded to %d bytes.\n", fd, s->max_wdata);
+ PRINTF("socket: %d wdata expanded to %d bytes.\n", fd, s->max_wdata);
}
if (s->wdata_size + len + 2048 < s->max_wdata)
s->wdata_size += len;
else
- fprintf(stderr, "socket: %d wdata lost !!\n", fd), abort();
+ FPRINTF(stderr, "socket: %d wdata lost !!\n", fd), abort();
}
void do_sendrecv(uint32_t next)
@@ -353,7 +354,7 @@ void do_parsepacket(void)
if (!session[i]->connected
&& time(NULL) - session[i]->created > CONNECT_TIMEOUT)
{
- printf("Session #%d timed out\n", i);
+ PRINTF("Session #%d timed out\n", i);
session[i]->eof = 1;
}
if (!session[i]->rdata_size && !session[i]->eof)
@@ -383,7 +384,7 @@ void RFIFOSKIP(int fd, size_t len)
if (s->rdata_size < s->rdata_pos)
{
- fprintf(stderr, "too many skip\n");
+ FPRINTF(stderr, "too many skip\n");
abort();
}
}
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 0215b53..004771c 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -8,6 +8,7 @@
#include <cstdlib>
#include <cstring>
+#include "cxxstdio.hpp"
#include "utils.hpp"
static
@@ -183,12 +184,12 @@ void delete_timer(timer_id id, timer_func func)
{
if (id == 0 || id >= timer_data_num)
{
- fprintf(stderr, "delete_timer error : no such timer %d\n", id);
+ FPRINTF(stderr, "delete_timer error : no such timer %d\n", id);
abort();
}
if (timer_data[id].func != func)
{
- fprintf(stderr, "Timer mismatch\n");
+ FPRINTF(stderr, "Timer mismatch\n");
abort();
}
// "to let them disappear" - is this just in case?
diff --git a/src/common/utils.cpp b/src/common/utils.cpp
index 35fdf5a..d8c0c12 100644
--- a/src/common/utils.cpp
+++ b/src/common/utils.cpp
@@ -1,11 +1,14 @@
#include "utils.hpp"
#include <netinet/in.h>
+#include <sys/time.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <algorithm>
+
//-----------------------------------------------------
// Function to suppress control characters in a string.
//-----------------------------------------------------
@@ -95,3 +98,58 @@ const char *ip2str(struct in_addr ip, bool extra_dot)
sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
return buf;
}
+
+bool split_key_value(const std::string& line, std::string *w1, std::string *w2)
+{
+ std::string::const_iterator begin = line.begin(), end = line.end();
+
+ if (line[0] == '/' && line[1] == '/')
+ return false;
+ if (line.back() == '\r')
+ --end;
+ if (line.empty())
+ return false;
+
+ if (std::find_if(begin, end,
+ [](unsigned char c) { return c < ' '; }
+ ) != line.end())
+ return false;
+ std::string::const_iterator colon = std::find(begin, end, ':');
+ if (colon == end)
+ return false;
+ w1->assign(begin, colon);
+ ++colon;
+ while (std::isspace(*colon))
+ ++colon;
+ w2->assign(colon, end);
+ return true;
+}
+
+void stamp_time(timestamp_seconds_buffer& out, time_t *t)
+{
+ time_t when = t ? *t : time(NULL);
+ strftime(out, sizeof(out), "%Y-%m-%d %H:%M:%S", gmtime(&when));
+}
+void stamp_time(timestamp_milliseconds_buffer& out)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ strftime(out, sizeof(out), "%Y-%m-%d %H:%M:%S", gmtime(&tv.tv_sec));
+ sprintf(out + 20, ".%03d", int(tv.tv_usec / 1000));
+}
+
+void log_with_timestamp(FILE *out, const_string line)
+{
+ if (!line)
+ {
+ fputc('\n', out);
+ return;
+ }
+ timestamp_milliseconds_buffer tmpstr;
+ stamp_time(tmpstr);
+ fwrite(tmpstr, 1, sizeof(tmpstr), out);
+ fputs(": ", out);
+ fwrite(line.data(), 1, line.size(), out);
+ if (line.back() != '\n')
+ fputc('\n', out);
+}
diff --git a/src/common/utils.hpp b/src/common/utils.hpp
index 76ac626..553d34f 100644
--- a/src/common/utils.hpp
+++ b/src/common/utils.hpp
@@ -3,7 +3,12 @@
#include "sanity.hpp"
-// unguarded!
+#include <cstdio>
+#include <cstring>
+
+#include <string>
+
+#include "const_array.hpp"
#include "utils2.hpp"
/*
@@ -26,4 +31,23 @@ int e_mail_check(const char *email);
int config_switch (const char *str);
const char *ip2str(struct in_addr ip, bool extra_dot = false);
+bool split_key_value(const std::string& line, std::string *w1, std::string *w2);
+
+inline
+void strzcpy(char *dest, const char *src, size_t n)
+{
+ if (n)
+ {
+ strncpy(dest, src, n);
+ dest[n-1] = '\0';
+ }
+}
+
+typedef char timestamp_seconds_buffer[20];
+typedef char timestamp_milliseconds_buffer[24];
+void stamp_time(timestamp_seconds_buffer&, time_t *t=nullptr);
+void stamp_time(timestamp_milliseconds_buffer&);
+
+void log_with_timestamp(FILE *out, const_string line);
+
#endif //UTILS_HPP