diff options
author | Ben Longbons <b.r.longbons@gmail.com> | 2012-12-27 21:23:46 -0800 |
---|---|---|
committer | Ben Longbons <b.r.longbons@gmail.com> | 2013-01-07 15:31:38 -0800 |
commit | c080e504e4d74027b985b1ed675c172c083cea76 (patch) | |
tree | ac084d16d9d40c0a0c950b66eb62a0e16795d486 /src/common | |
parent | ae30173d71d3bfc8514dbe70b6c90c9a3324b8fc (diff) | |
download | tmwa-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.hpp | 175 | ||||
-rw-r--r-- | src/common/cxxstdio.hpp | 123 | ||||
-rw-r--r-- | src/common/db.cpp | 4 | ||||
-rw-r--r-- | src/common/extract.hpp | 208 | ||||
-rw-r--r-- | src/common/grfio.cpp | 11 | ||||
-rw-r--r-- | src/common/lock.cpp | 17 | ||||
-rw-r--r-- | src/common/mmo.hpp | 15 | ||||
-rw-r--r-- | src/common/nullpo.cpp | 3 | ||||
-rw-r--r-- | src/common/nullpo.hpp | 1 | ||||
-rw-r--r-- | src/common/sanity.hpp | 23 | ||||
-rw-r--r-- | src/common/socket.cpp | 15 | ||||
-rw-r--r-- | src/common/timer.cpp | 5 | ||||
-rw-r--r-- | src/common/utils.cpp | 58 | ||||
-rw-r--r-- | src/common/utils.hpp | 26 |
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 |