Please take our Survey
logo       

Choosing A Webhost:
A web hosting service is a type of Internet hosting service that allows individuals and organizations to provide their own website accessible via the World Wide Web. Web hosts are companies that provide space on a server they own for use by their clients as well as providing Internet connectivity, typically in a data center. Web hosts can also provide data center space and connectivity to the Internet for servers they do not own to be located in their data center, called colocation. more...

CVS update of odbsequoia/src (7 files): msg#00094

db.carob.cvs

Subject: CVS update of odbsequoia/src (7 files)

Date: Friday, February 24, 2006 @ 16:12:43
Author: marc
Path: /cvsroot/carob/odbsequoia/src

Added: descriptors.cpp (1.1) descriptors.hpp (1.1)
descriptors_records.cpp (1.1) descriptors_records.hpp (1.1)
Modified: GNUmakefile (1.5 -> 1.6) stmt.cpp (1.13 -> 1.14) stmt.hpp (1.10
-> 1.11)

First draft implementation of SQLBindCol() and SQLFetch(). Features new
descriptor and descriptor_records classes.


-------------------------+
GNUmakefile | 12 ++-
descriptors.cpp | 152 +++++++++++++++++++++++++++++++++++++++++++
descriptors.hpp | 161 ++++++++++++++++++++++++++++++++++++++++++++++
descriptors_records.cpp | 60 +++++++++++++++++
descriptors_records.hpp | 116 +++++++++++++++++++++++++++++++++
stmt.cpp | 65 ++++++++++++++++++
stmt.hpp | 27 ++++++-
7 files changed, 586 insertions(+), 7 deletions(-)


Index: odbsequoia/src/GNUmakefile
diff -u odbsequoia/src/GNUmakefile:1.5 odbsequoia/src/GNUmakefile:1.6
--- odbsequoia/src/GNUmakefile:1.5 Thu Jan 12 23:42:37 2006
+++ odbsequoia/src/GNUmakefile Fri Feb 24 16:12:43 2006
@@ -29,7 +29,7 @@
CAROB=carob


-OBJS=util.o explicit_type.o env.o connect.o stmt.o abstract_item.o
+OBJS=util.o explicit_type.o env.o connect.o stmt.o abstract_item.o
descriptors.o descriptors_records.o
CXXFLAGS=-Wall -g3 -I${CAROB_PATH}/include

all: a.out
@@ -43,11 +43,17 @@

# libcarob.a needs -lpthread
${LIB}: ${OBJS} ${CAROB_PATH}/lib${CAROB}.a
- c++ ${CXXFLAGS} -shared $^ -lpthread -lodbcinst -o $@
+ ${CXX} ${CXXFLAGS} -shared $^ -lpthread -lodbcinst -o $@

clean:
rm -f ${LIB} ${LIBSHORT} a.out ${OBJS}

# useful to detect linking issues *before* the driver manager gets them
linktest: ${LIBSHORT}
- c++ dummymain.cpp ${LIBSHORT} -lodbc -lodbcinst -o $@
+ ${CXX} dummymain.cpp ${LIBSHORT} -lodbc -lodbcinst -o $@
+
+# compile each .hpp, isolated
+checkheaders:
+ for h in *.hpp; do ln -sf $$h someheader.cpp; \
+ printf "compiling $$h\n"; \
+ ${CXX} -c ${CXXFLAGS} someheader.cpp; done
Index: odbsequoia/src/descriptors.cpp
diff -u /dev/null odbsequoia/src/descriptors.cpp:1.1
--- /dev/null Fri Feb 24 16:12:43 2006
+++ odbsequoia/src/descriptors.cpp Fri Feb 24 16:12:43 2006
@@ -0,0 +1,152 @@
+/*
+ * Sequoia: Database clustering technology.
+ * Copyright (C) 2006 Continuent, Inc.
+ * Contact: sequoia-NAAfj4rwCWAYtQj7fl1lsA@xxxxxxxxxxxxxxxx
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Initial developer(s): Marc Herbert
+ * Contributor(s):
+ */
+
+
+#include "descriptors.hpp"
+
+#include "util.hpp"
+
+// scatter these classes into multiple files when this file becomes too big
+
+using namespace ODBSeqNS;
+
+/*** BaseDesc methods ***/
+
+ODBCBaseDesc::~ODBCBaseDesc()
+{
+ // unbind all remaining records
+ for (std::vector<BaseDescRecord *>::const_iterator reci = records.begin();
+ reci != records.end(); reci++)
+ if (*reci != 0)
+ delete *reci;
+
+}
+
+SQLRETURN
+ODBCBaseDesc::resize(size_t newsize)
+{
+ if (newsize > records.capacity())
+ records.reserve(4 * records.capacity());
+ records.resize(newsize, 0);
+
+ return SQL_SUCCESS;
+}
+
+
+/*** AppDesc methods ***/
+
+ODBCAppDesc::~ODBCAppDesc()
+{
+ // TODO: if we were alloc'ed by user, browse all statements owned
+ // by our connection and re-tie them to their default AppDesc.
+}
+
+SQLRETURN
+ODBCAppDesc::unbind_record(size_t recnum)
+{
+ delete records.at(recnum);
+
+ if (recnum == count() // unbounding highest record
+ && recnum > 0 ) // unbounding a regular record (not a bookmark)
+ {
+ // adjustment needed
+ SQLUSMALLINT newcount = recnum-1;
+ while (newcount > 0 && records[newcount] == 0)
+ newcount--;
+
+ recount(newcount);
+ }
+
+ return SQL_SUCCESS;
+
+}
+
+
+SQLRETURN
+ODBCAppDesc::bind_col(SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ SQLPOINTER TargetValuePtr, SQLINTEGER BufferLength,
+ SQLLEN * StrLen_or_Ind)
+{
+ if (ColumnNumber > count())
+ {
+ recount(ColumnNumber);
+ records.at(ColumnNumber) = new AppDescRecord();
+ }
+
+ AppDescRecord * apprec = static_cast<AppDescRecord
*>(records[ColumnNumber]);
+
+ return apprec->bind_col(TargetType, TargetValuePtr, BufferLength,
StrLen_or_Ind);
+}
+
+
+/** Fetch the current row of the DriverResultSet into our buffers */
+SQLRETURN
+ODBCAppDesc::fetchRow(CarobNS::DriverResultSet& rs)
+{
+ // FIXME: work in progress, not even half-implemented
+
+ int rs_col_end = rs.getNumberOfColumns() + 1;
+
+ std::vector<BaseDescRecord *>::const_iterator rec_col = records.begin();
+ rec_col++; // skip bookmark 0
+ int rs_col = 1;
+ for ( ;
+ rec_col != records.end() && rs_col < rs_col_end;
+ rec_col++, rs_col++)
+ {
+ if (*rec_col != 0) // is this row bound ? Else ignore it
+ {
+ AppDescRecord& rec = * static_cast<AppDescRecord *>(*rec_col);
+
+ if (rs.isNull(rs_col))
+ {
+ if (0 == rec.indicator_ptr)
+ throw ODBSeqException(L"22002",
+ L"Cannot flag SQL_NULL_DATA"
+ L"because indicator_ptr is null");
+
+ * rec.indicator_ptr = SQL_NULL_DATA;
+ continue;
+ }
+
+ // case (type == SQL_C_INT)
+ * static_cast<int *>(rec.data_ptr) = rs.getAsInt(rs_col);
+ if (rec.octet_length_ptr != 0)
+ * rec.octet_length_ptr = sizeof(int);
+
+ }
+ }
+
+ // if (rec_col != records.end())
+ // not enough columns for the descriptor!!
+ // Unfortunately we should have reported this error at SQLBindCol()
time
+
+
+ return SQL_SUCCESS;
+}
+
+/*
+ * Local Variables:
+ * c-file-style: "bsd"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
Index: odbsequoia/src/descriptors.hpp
diff -u /dev/null odbsequoia/src/descriptors.hpp:1.1
--- /dev/null Fri Feb 24 16:12:43 2006
+++ odbsequoia/src/descriptors.hpp Fri Feb 24 16:12:43 2006
@@ -0,0 +1,161 @@
+
+/*
+ * Sequoia: Database clustering technology.
+ * Copyright (C) 2006 Continuent, Inc.
+ * Contact: sequoia-NAAfj4rwCWAYtQj7fl1lsA@xxxxxxxxxxxxxxxx
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Initial developer(s): Marc Herbert
+ * Contributor(s):
+ */
+
+#ifndef ODBSEQ_DESCRIPTORS
+#define ODBSEQ_DESCRIPTORS
+
+#include "abstract_item.hpp"
+
+#include "descriptors_records.hpp"
+
+#include "DriverResultSet.hpp"
+
+#include <vector>
+
+#include <sql.h>
+#include <sqlucode.h>
+
+class CarobNS::DriverResultSet;
+
+namespace ODBSeqNS {
+
+
+/** Base class for descriptors */
+
+class ODBCBaseDesc : public ODBCItem
+{
+
+public:
+
+ size_t
+ size() { return records.size(); }
+
+ size_t
+ count() { return (this->size() - 1); }
+
+ virtual
+ ~ODBCBaseDesc();
+
+protected:
+ /** Only derived classes can be constructed */
+ ODBCBaseDesc(const ODBCItem &creator, bool explicit_alloc) :
+ ODBCItem(creator),
+ alloc_user(explicit_alloc),
+ // column 0 (bookmark) is always there, even when not supported and
NULL
+ records(1, static_cast<BaseDescRecord*>(0))
+ { }
+
+ /** Warning: caller must manually allocate/delete pointed-to records! */
+ SQLRETURN
+ resize(size_t newsize);
+
+ SQLRETURN
+ recount(size_t newcount) { return resize(newcount + 1); }
+
+ const bool alloc_user; // SQL_DESC_ALLOC_USER / SQL_DESC_ALLOC_AUTO
+
+protected:
+ std::vector<BaseDescRecord *> records;
+
+private:
+ // forbid copy ctor & assignement because of the pointers to records
+ ODBCBaseDesc(const ODBCBaseDesc& orig);
+ ODBCBaseDesc& operator=(const ODBCBaseDesc& orig);
+
+};
+
+
+/** Application Row/Parameter Descriptor */
+
+class ODBCAppDesc : public ODBCBaseDesc
+{
+public:
+
+ ODBCAppDesc(const ODBCItem &creator, bool explicit_alloc) :
+ ODBCBaseDesc(creator, explicit_alloc)
+ { };
+
+ ~ODBCAppDesc();
+
+ /** Takes care of size (count) adjustment */
+ SQLRETURN
+ unbind_record(size_t col);
+
+ /** Takes care of size (count) adjustment */
+ SQLRETURN
+ bind_col(SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ SQLPOINTER TargetValuePtr, SQLINTEGER BufferLength,
+ SQLLEN * StrLen_or_Ind);
+
+ SQLRETURN
+ ODBCAppDesc::fetchRow(CarobNS::DriverResultSet& rs);
+
+};
+
+
+/** Abstract Implementation Descriptor */
+
+class ODBCImplDesc : public ODBCBaseDesc
+{
+
+protected:
+ ODBCImplDesc(const ODBCItem& creator) :
+ ODBCBaseDesc(creator, false)
+ { } ;
+};
+
+/** Implementation Row Descriptor */
+
+class ODBCImplRowDesc : public ODBCImplDesc
+{
+public:
+ ODBCImplRowDesc(const ODBCItem& creator) :
+ ODBCImplDesc(creator)
+ { } ;
+};
+
+/** Implementation Parameter Descriptor */
+
+class ODBCImplParamDesc : public ODBCImplDesc
+{
+public:
+ ODBCImplParamDesc(const ODBCItem& creator) :
+ ODBCImplDesc(creator)
+ { } ;
+};
+
+
+} // namespace ODBSeqNS
+
+
+#endif // include only once
+
+
+
+/*
+ * Local Variables:
+ * c-file-style: "bsd"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
Index: odbsequoia/src/descriptors_records.cpp
diff -u /dev/null odbsequoia/src/descriptors_records.cpp:1.1
--- /dev/null Fri Feb 24 16:12:43 2006
+++ odbsequoia/src/descriptors_records.cpp Fri Feb 24 16:12:43 2006
@@ -0,0 +1,60 @@
+/*
+ * Sequoia: Database clustering technology.
+ * Copyright (C) 2006 Continuent, Inc.
+ * Contact: sequoia-NAAfj4rwCWAYtQj7fl1lsA@xxxxxxxxxxxxxxxx
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Initial developer(s): Marc Herbert
+ * Contributor(s):
+ */
+
+
+#include "descriptors_records.hpp"
+
+using namespace ODBSeqNS;
+
+/**** Base records ***/
+
+SQLRETURN
+BaseDescRecord::set_type(SQLSMALLINT concise_arg)
+{
+ // FIXME: set all other interdependent fields
+ concise_type = concise_arg;
+ return SQL_SUCCESS;
+}
+
+/**** Application records ***/
+
+SQLRETURN
+AppDescRecord::bind_col(SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr,
+ SQLINTEGER BufferLength, SQLLEN * StrLen_or_Ind)
+{
+ set_type(TargetType);
+
+ octet_length = BufferLength;
+ data_ptr = TargetValuePtr;
+
+ indicator_ptr = StrLen_or_Ind;
+ octet_length_ptr = StrLen_or_Ind;
+
+ return SQL_SUCCESS;
+}
+
+/*
+ * Local Variables:
+ * c-file-style: "bsd"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
Index: odbsequoia/src/descriptors_records.hpp
diff -u /dev/null odbsequoia/src/descriptors_records.hpp:1.1
--- /dev/null Fri Feb 24 16:12:43 2006
+++ odbsequoia/src/descriptors_records.hpp Fri Feb 24 16:12:43 2006
@@ -0,0 +1,116 @@
+
+/*
+ * Sequoia: Database clustering technology.
+ * Copyright (C) 2006 Continuent, Inc.
+ * Contact: sequoia-NAAfj4rwCWAYtQj7fl1lsA@xxxxxxxxxxxxxxxx
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Initial developer(s): Marc Herbert
+ * Contributor(s):
+ */
+
+#ifndef ODBSEQ_DESC_RECORDS
+#define ODBSEQ_DESC_RECORDS
+
+
+#include <sql.h>
+#include <sqlucode.h>
+
+
+namespace ODBSeqNS {
+
+
+/** Part of records common to all descriptors */
+
+class BaseDescRecord
+{
+public:
+ // Very simple fields are public to avoid cluttering the code with
+ // getters/setters You did not believe descriptors and records
+ // classes are well separated by a clear interface, did you?
+ SQLINTEGER octet_length;
+
+ /** Sets the concise type as well as all other interdependent fields */
+ SQLRETURN
+ set_type(SQLSMALLINT concise_arg);
+
+ virtual ~BaseDescRecord() { }
+
+private:
+ // Those are private because we want to enforce some consistency.
+ // concise = type + datetime_interval_code, see
+ // table in "Data Type Identifiers and Descriptors" ODBC page.
+ SQLSMALLINT concise_type;
+// SQLSMALLINT type;
+// SQLSMALLINT datetime_interval_code;
+};
+
+/** Derived class specific to Application Descriptors */
+
+class AppDescRecord : public BaseDescRecord
+{
+public:
+ /** Default constructor */
+ AppDescRecord() : data_ptr(0) { }
+
+ SQLRETURN
+ bind_col(SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr,
+ SQLINTEGER BufferLength, SQLLEN * StrLen_or_Ind);
+
+public: // public to avoid cluttering the code with getters/setters
+ SQLPOINTER data_ptr;
+ /** Warning: null is allowed. Check SQLBindCol() documentation */
+ SQLINTEGER * indicator_ptr;
+ /** Warning: null is allowed. Check SQLBindCol() documentation */
+ SQLINTEGER * octet_length_ptr;
+};
+
+/** Derived class specific to Implementation Descriptors */
+
+class ImplDescRecord : public BaseDescRecord
+{
+
+};
+
+/** Derived class for Implementation Row Descriptor record */
+
+class ImplRowDescRecord : public ImplDescRecord
+{
+
+};
+
+/** Derived class for Implementation Parameter Descriptor record */
+
+class ImplParamDescRecord : public ImplDescRecord
+{
+
+};
+
+
+
+} // namespace ODBSeqNS
+
+
+#endif // include only once
+
+
+
+/*
+ * Local Variables:
+ * c-file-style: "bsd"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
Index: odbsequoia/src/stmt.cpp
diff -u odbsequoia/src/stmt.cpp:1.13 odbsequoia/src/stmt.cpp:1.14
--- odbsequoia/src/stmt.cpp:1.13 Fri Feb 24 16:08:55 2006
+++ odbsequoia/src/stmt.cpp Fri Feb 24 16:12:43 2006
@@ -83,6 +83,71 @@
return SQL_SUCCESS;
}

+SQLRETURN
+SQLBindCol(SQLHSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ SQLPOINTER TargetValuePtr, SQLINTEGER BufferLength,
+ SQLLEN * StrLen_or_Ind)
+{
+ ODBCStatement * self_p = objectify(StatementHandle);
+ self_p->clear_diags();
+
+ _PROTECT_SQLRETURN(self_p,
+ bind_col(ColumnNumber, TargetType, TargetValuePtr,
+ BufferLength, StrLen_or_Ind));
+}
+
+SQLRETURN
+ODBCStatement::bind_col(SQLUSMALLINT col, SQLSMALLINT type,
+ SQLPOINTER buf, SQLINTEGER buflen,
+ SQLLEN * len_or_ind)
+{
+ if (0 == buf) // unbinding
+ {
+ if (col < ARD->size())
+ return ARD->unbind_record(col);
+ // else the spec does not ask for an error?
+ }
+
+ SQLRETURN retcode = ARD->bind_col(col, type, buf, buflen, len_or_ind);
+
+ return retcode;
+}
+
+SQLRETURN
+SQLFetch(SQLHSTMT StatementHandle)
+{
+ ODBCStatement * self_p = objectify(StatementHandle);
+ self_p->clear_diags();
+
+ _PROTECT_SQLRETURN(self_p, fetch());
+
+}
+
+SQLRETURN
+ODBCStatement::fetch()
+{
+ // FIXME: work in progress, not even half-implemented
+
+ if (this->state == S4)
+ throw ODBSeqException(L"24000",
+ L"No ResultSet available");
+
+ if (this->state != S5 && this->state != S6)
+ throw ODBSeqException(L"HY010",
+ L"Invalid state, no current ResultSet"
+ " (TODO: print current state)");
+
+ // FIXME: catch exception to set the correct SQLState here?
+ // Or rather ask carob to set it.
+ carob_stmt->getResultSet()->next();
+
+ if (this->state == S5)
+ this->state = S6;
+
+ return ARD->fetchRow(*(carob_stmt->getResultSet()));
+
+}

SQLRETURN
ODBCStatement::get_header_diag_fieldw(SQLSMALLINT DiagIdentifier,
Index: odbsequoia/src/stmt.hpp
diff -u odbsequoia/src/stmt.hpp:1.10 odbsequoia/src/stmt.hpp:1.11
--- odbsequoia/src/stmt.hpp:1.10 Fri Feb 24 15:55:45 2006
+++ odbsequoia/src/stmt.hpp Fri Feb 24 16:12:43 2006
@@ -19,8 +19,10 @@
* Contributor(s):
*/

-#ifndef ODBSEQ_NS
-#define ODBSEQ_NS
+#ifndef ODBSEQ_STMT
+#define ODBSEQ_STMT
+
+#include "descriptors.hpp"

#include "connect.hpp"

@@ -31,7 +33,7 @@

namespace ODBSeqNS {

-enum stmt_state_t { S0, S1, S4, S5 };
+ enum stmt_state_t { S0, S1, S4, S5, S6 };

class ODBCStatement : public ODBCItem
{
@@ -40,11 +42,20 @@
stmt_state_t state; // state according to "Statement Transitions"
// in the ODBC reference (as far as
// possible).
- ODBCStatement(const ODBCConnection& creator) : ODBCItem(creator)
+ ODBCStatement(const ODBCConnection& creator) :
+ ODBCItem(creator),
+ IRD(*this), IPD(*this),
+ implicit_ARD(*this, false), implicit_APD(*this, false),
+ ARD(&implicit_ARD), APD(&implicit_APD)
{ state = S0; };
SQLRETURN
exec_directw(const SQLWCHAR *StatementText, SQLINTEGER TextLength);

+ SQLRETURN
+ bind_col(SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ SQLPOINTER TargetValuePtr, SQLINTEGER BufferLength,
+ SQLLEN * StrLen_or_Ind);
+
const std::wstring&
get_carob_diagids() const
{ // the owning connection has the prefix/knows the source
@@ -72,6 +83,14 @@
SQLPOINTER DiagInfoPtr,
SQLSMALLINT BufferLength, SQLSMALLINT *
StringLengthPtr)
const;
+
+private:
+ ODBCImplParamDesc IRD;
+ ODBCImplRowDesc IPD;
+ ODBCAppDesc implicit_ARD;
+ ODBCAppDesc implicit_APD;
+ ODBCAppDesc *ARD, *APD; // pointers because these one can be overriden
+
};


<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

Free Magazines

Cisco News
Receive a free quarterly e-newsletter with exclusive articles on how Cisco IT uses its own products and solutions to enable the business.
subscribe

Systems Management News, the newspaper for IT systems administration and data center managers! Each issue of Systems Management News is chock-full of news and analysis to help you understand what's happening in your field.
subscribe

The Enterprise Newsweekly eWeek is the essential technology information source for builders of e-business.
subscribe

Oracle Magazine Oracle Magazine contains technology strategy articles, sample code, tips, Oracle and partner news, how to articles for developers and DBAs, and more. Oracle (NASDAQ: ORCL) is the world's largest enterprise software company.
subscribe

Total Telecom Total Telecom is "The Economist of the communications industry".
subscribe

Navigation

Home | advertise | OSDir is an inevitable website. super tiny logo