libodbc++  0.2.5
types.h
1 /*
2  This file is part of libodbc++.
3 
4  Copyright (C) 1999-2000 Manush Dodunekov <manush@stendahls.net>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING. If not, write to
18  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  Boston, MA 02111-1307, USA.
20 */
21 
22 #ifndef __ODBCXX_TYPES_H
23 #define __ODBCXX_TYPES_H
24 
25 #include <odbc++/setup.h>
26 
27 #include <cstring>
28 #include <exception>
29 #include <assert.h>
30 
31 #if !defined(ODBCXX_QT)
32 # include <string>
33 # else
34 # include <qstring.h>
35 #endif
36 
37 #include <ctime>
38 #if defined(ODBCXX_NO_STD_TIME_T)
39 namespace std {
40  using ::time_t;
41 };
42 #endif
43 
44 #if defined(ODBCXX_QT)
45 class QIODevice;
46 #endif
47 
48 #if defined(ODBCXX_HAVE_ISQL_H) && defined(ODBCXX_HAVE_ISQLEXT_H)
49 # include <isql.h>
50 # include <isqlext.h>
51 #elif defined(ODBCXX_HAVE_SQL_H) && defined(ODBCXX_HAVE_SQLEXT_H)
52 # include <sql.h>
53 # include <sqlext.h>
54 #else
55 # error "Whoops. Can not recognize the ODBC subsystem."
56 #endif
57 
58 #if defined(ODBCXX_HAVE_SQLUCODE_H)
59 # include <sqlucode.h>
60 #endif
61 
62 #if defined(ODBCXX_HAVE_DB2SQLCLI_H)
63 #include <sqlcli.h>
64 #endif
65 // fixups for current iODBC, which kindly doesn't provide SQL_TRUE and
66 // SQL_FALSE macros
67 
68 #if !defined(SQL_TRUE)
69 # define SQL_TRUE 1
70 #endif
71 
72 #if !defined(SQL_FALSE)
73 # define SQL_FALSE 0
74 #endif
75 
76 // MS ODBC SDK misses this in some releases
77 #if ODBCVER >= 0x0300 && !defined(SQL_NOT_DEFERRABLE)
78 # define SQL_NOT_DEFERRABLE 7
79 #endif
80 
81 
82 // Setup our ODBC3_C (odbc3 conditional) macro
83 #if ODBCVER >= 0x0300
84 
85 # define ODBC3_C(odbc3_value,old_value) odbc3_value
86 
87 #else
88 
89 # define ODBC3_C(odbc3_value,old_value) old_value
90 
91 #endif
92 
93 
94 // ODBC3_DC (odbc3 dynamic conditional)
95 // Every context using this macro should provide
96 // a this->_getDriverInfo() method returning
97 // a const DriverInfo*
98 
99 #if ODBCVER >= 0x0300
100 
101 # define ODBC3_DC(odbc3_value,old_value) \
102 (this->_getDriverInfo()->getMajorVersion()>=3?odbc3_value:old_value)
103 
104 #else
105 
106 # define ODBC3_DC(odbc3_value,old_value) old_value
107 
108 #endif
109 
110 #if defined(ODBCXX_HAVE_INTTYPES_H)
111 # include <inttypes.h>
112 #endif
113 
114 #include <vector>
115 #include <map>
116 
117 namespace odbc {
118 
119  // We want Long to be at least 64 bits
120 
121 #if defined(WIN32)
122 
123  typedef __int64 Long;
124 
125 #define DATASTATUS_TYPE SQLLEN
126 //#define DATASTATUS_TYPE SQLINTEGER
127 
128 #elif defined(ODBCXX_HAVE_INTTYPES_H)
129 
130  typedef int64_t Long;
131 #define DATASTATUS_TYPE SQLLEN
132 #else
133 #define DATASTATUS_TYPE SQLINTEGER
134 # if ODBCXX_SIZEOF_INT == 8
135 
136  typedef int Long;
137 
138 # elif ODBCXX_SIZEOF_LONG == 8
139 
140  typedef long Long;
141 
142 # elif ODBCXX_SIZEOF_LONG_LONG == 8
143 
144  typedef long long Long;
145 
146 # else
147 
148 # error "Can't find an appropriate at-least-64-bit integer"
149 
150 # endif
151 
152 #endif
153 
154 
155  //constants:
156  //how much we try to fetch with each SQLGetData call
157  const int GETDATA_CHUNK_SIZE=4*1024;
158  //how much we write with each SQLPutData call
159  const int PUTDATA_CHUNK_SIZE=GETDATA_CHUNK_SIZE;
160 
161  //how much we read/write in string<->stream conversion
162  //better names for those?
163  const int STRING_TO_STREAM_CHUNK_SIZE=1024;
164  const int STREAM_TO_STRING_CHUNK_SIZE=STRING_TO_STREAM_CHUNK_SIZE;
165 
166 
167 
168 
169 
172  struct Types {
175  enum SQLType {
177  UNKNOWN_TYPE = SQL_UNKNOWN_TYPE,
179  BIGINT = SQL_BIGINT,
181  BINARY = SQL_BINARY,
183  BIT = SQL_BIT,
185  CHAR = SQL_CHAR,
187  DATE = ODBC3_C(SQL_TYPE_DATE,SQL_DATE),
189  DECIMAL = SQL_DECIMAL,
191  DOUBLE = SQL_DOUBLE,
193  FLOAT = SQL_FLOAT,
195  INTEGER = SQL_INTEGER,
197  LONGVARBINARY = SQL_LONGVARBINARY,
199  LONGVARCHAR = SQL_LONGVARCHAR,
201  NUMERIC = SQL_NUMERIC,
203  REAL = SQL_REAL,
205  SMALLINT = SQL_SMALLINT,
207  TIME = ODBC3_C(SQL_TYPE_TIME,SQL_TIME),
209  TIMESTAMP = ODBC3_C(SQL_TYPE_TIMESTAMP,SQL_TIMESTAMP),
211  TINYINT = SQL_TINYINT,
213  VARBINARY = SQL_VARBINARY,
215  VARCHAR = SQL_VARCHAR
216 #if defined(ODBCXX_HAVE_SQLUCODE_H)
217  ,
219  WCHAR = SQL_WCHAR,
221  WVARCHAR = SQL_WVARCHAR,
223  WLONGVARCHAR = SQL_WLONGVARCHAR
224 #endif
225 #if (ODBCVER >= 0x0350)
226  ,
227  GUID = SQL_GUID
228 #endif
229  };
230  };
231 
232 
233 #if !defined(ODBCXX_QT)
234 
240  class ODBCXX_EXPORT Bytes {
241  private:
242  struct Rep {
243  ODBCXX_SIGNED_CHAR_TYPE* buf_;
244  size_t len_;
245  int refCount_;
246  Rep(const ODBCXX_SIGNED_CHAR_TYPE* b, size_t l)
247  :len_(l), refCount_(0) {
248  if(len_>0) {
249  buf_= ODBCXX_OPERATOR_NEW_DEBUG(__FILE__, __LINE__) ODBCXX_SIGNED_CHAR_TYPE[len_];
250  memcpy((void*)buf_,(void*)b,len_);
251  } else {
252  buf_=NULL;
253  }
254  }
255  ~Rep() {
256  delete [] buf_;
257  }
258  };
259 
260  Rep* rep_;
261  public:
264  :rep_(ODBCXX_OPERATOR_NEW_DEBUG(__FILE__, __LINE__) Rep(NULL,0)) {
265  rep_->refCount_++;
266  }
267 
269  Bytes(const ODBCXX_SIGNED_CHAR_TYPE* data, size_t dataLen)
270  :rep_(ODBCXX_OPERATOR_NEW_DEBUG(__FILE__, __LINE__) Rep(data,dataLen)) {
271  rep_->refCount_++;
272  }
273 
275  Bytes(const Bytes& b)
276  :rep_(b.rep_) {
277  rep_->refCount_++;
278  }
279 
281  Bytes& operator=(const Bytes& b) {
282  if(--rep_->refCount_==0) {
283  ODBCXX_OPERATOR_DELETE_DEBUG(__FILE__, __LINE__) rep_;
284  }
285  rep_=b.rep_;
286  rep_->refCount_++;
287  return *this;
288  }
289 
291  bool operator==(const Bytes& b) const {
292  if (getSize()!=b.getSize())
293  return false;
294  for(size_t i=0;i<getSize();i++) {
295  if(*(getData()+i)!=*(b.getData()+i))
296  return false;
297  }
298  return true;
299  }
300 
302  ~Bytes() {
303  if(--rep_->refCount_==0) {
304  ODBCXX_OPERATOR_DELETE_DEBUG(__FILE__, __LINE__) rep_;
305  }
306  }
307 
309  const ODBCXX_SIGNED_CHAR_TYPE* getData() const {
310  return rep_->buf_;
311  }
312 
314  size_t getSize() const {
315  return rep_->len_;
316  }
317  };
318 #endif
319 
320 #if defined(ODBCXX_HAVE_STRUCT_GUID)
321 
325  class ODBCXX_EXPORT Guid {
326  private:
327  ODBCXX_SIGNED_CHAR_TYPE data_[16];
328 
329  public:
331  Guid() {
332  for( size_t x = 0; x < 16; ++x ) {
333  data_[x] = 0;
334  }
335  }
336 
338  Guid(const ODBCXX_SIGNED_CHAR_TYPE* data, size_t dataLen) {
339  // data lenth should always be 16 for raw data
340  if (dataLen != 16)
341  return;
342 
343  // copy into local store
344  for (size_t x = 0; x < dataLen; ++x) {
345  data_[x] = data[x];
346  }
347  }
348 
349 
351  Guid(const Guid& b) {
352  for (int x = 0; x < 16; ++x) {
353  data_[x] = b.data_[x];
354  }
355  }
356 
358  bool operator==(const Guid& b) const {
359  // iterate through all of the items,
360  // we know it has to be 16 in length
361  for (size_t x=0; x < 16; ++x) {
362  // check for any difference
363  if(data_[x] != b.data_[x])
364  // if there is abort
365  return false;
366  }
367  // else if all the same, then true;
368  return true;
369  }
370 
372  ~Guid() { }
373 
375  const ODBCXX_SIGNED_CHAR_TYPE* getData() const {
376  return (ODBCXX_SIGNED_CHAR_TYPE *)data_;
377  }
378 
380  size_t getSize() const {
381  return 16;
382  }
383 
385  Bytes getBytes() const {
386  return Bytes(getData(), getSize());
387  }
388 
392  const _GUID toStruct() const {
393  _GUID structGuid;
394  // not exactly sure why this is such a pain, the rest worked fine.
395  // but to be paranoid, i'm going to do them all. except for data4.
396  structGuid.Data1 = ((unsigned char)data_[3] << 24)
397  | ((unsigned char)data_[2] << 16)
398  | ((unsigned char)data_[1] << 8)
399  | (unsigned char)data_[0];
400  structGuid.Data2 = (data_[5] << 8) | (unsigned char)data_[4];
401  structGuid.Data3 = (data_[7] << 8) | (unsigned char)data_[6];
402  structGuid.Data4[0] = data_[8];
403  structGuid.Data4[1] = data_[9];
404  structGuid.Data4[2] = data_[10];
405  structGuid.Data4[3] = data_[11];
406  structGuid.Data4[4] = data_[12];
407  structGuid.Data4[5] = data_[13];
408  structGuid.Data4[6] = data_[14];
409  structGuid.Data4[7] = data_[15];
410  return structGuid;
411  }
412  private:
413  const ODBCXX_STRING CharToHexStr(ODBCXX_SIGNED_CHAR_TYPE item) const {
414  // This method is mainly to make conversion to a string char
415  // much more easy and readable. Was having issues with some
416  // over run bits with GCC 4, When given 0xFF to strstream,
417  // I was getting 0xFFFFFFFF in return. This mandates last 2 chars.
418  ODBCXX_SSTREAM stream;
419  stream << std::hex << 0 << 0;
420  stream << static_cast<int>(item);
421  ODBCXX_STRING result = stream.str();
422  return result.substr(result.size() - 2, 2);
423  }
424  public:
425  const ODBCXX_STRING toString() const {
426  // create string from data, follow ole2 style conventions
427  // TODO: Check edianness of this conversion
428  ODBCXX_SSTREAM stream;
429  stream // construct string from data
430  << ODBCXX_STRING_CONST("{")
431  << CharToHexStr(data_[3])
432  << CharToHexStr(data_[2])
433  << CharToHexStr(data_[1])
434  << CharToHexStr(data_[0])
435  << ODBCXX_STRING_CONST("-")
436  << CharToHexStr(data_[5])
437  << CharToHexStr(data_[4])
438  << ODBCXX_STRING_CONST("-")
439  << CharToHexStr(data_[7])
440  << CharToHexStr(data_[6])
441  << ODBCXX_STRING_CONST("-")
442  << CharToHexStr(data_[8])
443  << CharToHexStr(data_[9])
444  << CharToHexStr(data_[10])
445  << CharToHexStr(data_[11])
446  << CharToHexStr(data_[12])
447  << CharToHexStr(data_[13])
448  << CharToHexStr(data_[14])
449  << CharToHexStr(data_[15])
450  << ODBCXX_STRING_CONST("}");
451  return stream.str();
452  }
453  };
454 #endif
455 
457  class ODBCXX_EXPORT Date {
458  protected:
459  int year_;
460  int month_;
461  int day_;
462 
463  virtual void _invalid(const ODBCXX_CHAR_TYPE* what, int value);
464 
465  int _validateYear(int y) {
466  return y;
467  }
468 
469  int _validateMonth(int m) {
470  if(m<1 || m>12) {
471  this->_invalid(ODBCXX_STRING_CONST("month"),m);
472  }
473  return m;
474  }
475 
476  int _validateDay(int d) {
477  if(d<1 || d>31) {
478  this->_invalid(ODBCXX_STRING_CONST("day"),d);
479  }
480  return d;
481  }
482 
483  public:
486  Date(int year, int month, int day) {
487  this->setYear(year);
488  this->setMonth(month);
489  this->setDay(day);
490  }
491 
496  explicit Date();
497 
502  Date(std::time_t t) {
503  this->setTime(t);
504  }
505 
510  Date(const ODBCXX_STRING& str) {
511  this->parse(str);
512  }
513 
515  Date(const Date& d)
516  :year_(d.year_),
517  month_(d.month_),
518  day_(d.day_) {}
519 
521  Date& operator=(const Date& d) {
522  year_=d.year_;
523  month_=d.month_;
524  day_=d.day_;
525  return *this;
526  }
527 
529  virtual ~Date() {}
530 
532  virtual void setTime(std::time_t t);
533 
535  std::time_t getTime() const;
536 
538  void parse(const ODBCXX_STRING& str);
539 
541  int getYear() const {
542  return year_;
543  }
544 
546  int getMonth() const {
547  return month_;
548  }
549 
551  int getDay() const {
552  return day_;
553  }
554 
556  void setYear(int year) {
557  year_=this->_validateYear(year);
558  }
559 
561  void setMonth(int month) {
562  month_=this->_validateMonth(month);
563  }
564 
566  void setDay(int day) {
567  day_=this->_validateDay(day);
568  }
569 
571  virtual ODBCXX_STRING toString() const;
572  };
573 
575  class ODBCXX_EXPORT Time {
576  protected:
577  int hour_;
578  int minute_;
579  int second_;
580 
581  virtual void _invalid(const ODBCXX_CHAR_TYPE* what, int value);
582 
583  int _validateHour(int h) {
584  if(h<0 || h>23) {
585  this->_invalid(ODBCXX_STRING_CONST("hour"),h);
586  }
587  return h;
588  }
589 
590  int _validateMinute(int m) {
591  if(m<0 || m>59) {
592  this->_invalid(ODBCXX_STRING_CONST("minute"),m);
593  }
594  return m;
595  }
596 
597  int _validateSecond(int s) {
598  if(s<0 || s>61) {
599  this->_invalid(ODBCXX_STRING_CONST("second"),s);
600  }
601  return s;
602  }
603 
604  public:
606  Time(int hour, int minute, int second) {
607  this->setHour(hour);
608  this->setMinute(minute);
609  this->setSecond(second);
610  }
611 
616  explicit Time();
617 
622  Time(std::time_t t) {
623  this->setTime(t);
624  }
625 
630  Time(const ODBCXX_STRING& str) {
631  this->parse(str);
632  }
633 
635  Time(const Time& t)
636  :hour_(t.hour_),
637  minute_(t.minute_),
638  second_(t.second_) {}
639 
641  Time& operator=(const Time& t) {
642  hour_=t.hour_;
643  minute_=t.minute_;
644  second_=t.second_;
645  return *this;
646  }
647 
649  virtual ~Time() {}
650 
652  virtual void setTime(std::time_t t);
653 
655  std::time_t getTime() const;
656 
658  void parse(const ODBCXX_STRING& str);
659 
661  int getHour() const {
662  return hour_;
663  }
664 
666  int getMinute() const {
667  return minute_;
668  }
669 
671  int getSecond() const {
672  return second_;
673  }
674 
676  void setHour(int h) {
677  hour_=this->_validateHour(h);
678  }
679 
681  void setMinute(int m) {
682  minute_=this->_validateMinute(m);
683  }
684 
686  void setSecond(int s) {
687  second_=this->_validateSecond(s);
688  }
689 
690  virtual ODBCXX_STRING toString() const;
691  };
692 
693 
696  class ODBCXX_EXPORT Timestamp : public Date, public Time {
697  private:
698  int nanos_;
699 
700  virtual void _invalid(const ODBCXX_CHAR_TYPE* what, int value);
701 
702  int _validateNanos(int n) {
703  if(n<0) {
704  this->_invalid(ODBCXX_STRING_CONST("nanoseconds"),n);
705  }
706  return n;
707  }
708 
709  public:
711  Timestamp(int year, int month, int day,
712  int hour, int minute, int second,
713  int nanos =0)
714  :Date(year,month,day), Time(hour,minute,second) {
715  this->setNanos(nanos);
716  }
717 
722  explicit Timestamp();
723 
728  Timestamp(std::time_t t) {
729  this->setTime(t);
730  }
731 
736  Timestamp(const ODBCXX_STRING& s) {
737  this->parse(s);
738  }
739 
740 
743  :Date(t),Time(t),nanos_(t.nanos_) {}
744 
747  Date::operator=(t);
748  Time::operator=(t);
749  nanos_=t.nanos_;
750  return *this;
751  }
752 
754  virtual ~Timestamp() {}
755 
757  virtual void setTime(std::time_t t);
758 
760  virtual std::time_t getTime() const {
761  return Date::getTime()+Time::getTime();
762  }
763 
766  void parse(const ODBCXX_STRING& s);
767 
769  int getNanos() const {
770  return nanos_;
771  }
772 
774  void setNanos(int nanos) {
775  nanos_=this->_validateNanos(nanos);
776  }
777 
778  virtual ODBCXX_STRING toString() const;
779  };
780 
781 
782  //this is used for several 'lists of stuff' below
783  //expects T to be a pointer-to-something, and
784  //the contents will get deleted when the vector
785  //itself is deleted
786  template <class T> class CleanVector : public std::vector<T> {
787  private:
788  CleanVector(const CleanVector<T>&); //forbid
789  CleanVector<T>& operator=(const CleanVector<T>&); //forbid
790 
791  public:
792  explicit CleanVector() {}
793  virtual ~CleanVector() {
794  typename std::vector<T>::iterator i=this->begin();
795  typename std::vector<T>::iterator end=this->end();
796  while(i!=end) {
797  ODBCXX_OPERATOR_DELETE_DEBUG(__FILE__, __LINE__) *i;
798  ++i;
799  }
800  this->clear();
801  }
802  };
803 
804 
807  class ODBCXX_EXPORT DriverMessage {
808  friend class ErrorHandler;
809 
810  private:
811  ODBCXX_CHAR_TYPE state_[SQL_SQLSTATE_SIZE+1];
812  ODBCXX_CHAR_TYPE description_[SQL_MAX_MESSAGE_LENGTH];
813  SQLINTEGER nativeCode_;
814 
815  DriverMessage() {}
816 #if ODBCVER < 0x0300
817  static DriverMessage* fetchMessage(SQLHENV henv,
818  SQLHDBC hdbc,
819  SQLHSTMT hstmt);
820 #else
821  static DriverMessage* fetchMessage(SQLINTEGER handleType,
822  SQLHANDLE h,
823  int idx);
824 #endif
825 
826  public:
827  virtual ~DriverMessage() {}
828 
829  const ODBCXX_CHAR_TYPE* getSQLState() const {
830  return state_;
831  }
832 
833  const ODBCXX_CHAR_TYPE* getDescription() const {
834  return description_;
835  }
836 
837  int getNativeCode() const {
838  return nativeCode_;
839  }
840  };
841 
842 
845  class SQLException : public std::exception {
846  private:
847  ODBCXX_STRING reason_;
848  ODBCXX_STRING sqlState_;
849  int errorCode_;
850 #if defined(ODBCXX_UNICODE)
851  std::string reason8_;
852 #elif defined(ODBCXX_QT)
853  QCString reason8_;
854 #endif
855  public:
857  SQLException(const ODBCXX_STRING& reason =ODBCXX_STRING_CONST(""),
858  const ODBCXX_STRING& sqlState =ODBCXX_STRING_CONST(""),
859  int vendorCode =0)
860  :reason_(reason),
861  sqlState_(sqlState),
862  errorCode_(vendorCode)
863 #if defined(ODBCXX_UNICODE)
864 {
865  const size_t length =sizeof(wchar_t)*reason_.size();
866  char* temp =ODBCXX_OPERATOR_NEW_DEBUG(__FILE__, __LINE__) char[length+1];
867  wcstombs(temp,reason_.c_str(),length);
868  reason8_ =temp;
869  delete[] temp;
870 }
871 #else
872 # if defined(ODBCXX_QT)
873  ,reason8_(reason.local8Bit())
874 # endif
875 {}
876 #endif
877 
880  :reason_(dm.getDescription()),
881  sqlState_(dm.getSQLState()),
882  errorCode_(dm.getNativeCode()) {}
883 
885  virtual ~SQLException() throw() {}
886 
888  int getErrorCode() const {
889  return errorCode_;
890  }
891 
896  const ODBCXX_STRING& getSQLState() const {
897  return sqlState_;
898  }
899 
901  const ODBCXX_STRING& getMessage() const {
902  return reason_;
903  }
904 
905 
907  virtual const char* what() const throw() {
908  // the conversion from QString involves a temporary, which
909  // doesn't survive this scope. So here, we do a conditional
910 #if defined(ODBCXX_QT)
911  return reason8_.data();
912 #else
913 # if defined(ODBCXX_UNICODE)
914  return reason8_.c_str();
915 # else
916  return reason_.c_str();
917 # endif
918 #endif
919  }
921 static const ODBCXX_CHAR_TYPE* scDEFSQLSTATE;
923 static const ODBCXX_STRING ssDEFSQLSTATE;
924  };
925 
926 
931  class SQLWarning : public SQLException {
932 
933  SQLWarning(const SQLWarning&); //forbid
934  SQLWarning& operator=(const SQLWarning&); //forbid
935 
936  public:
938  SQLWarning(const ODBCXX_STRING& reason = ODBCXX_STRING_CONST(""),
939  const ODBCXX_STRING& sqlState = ODBCXX_STRING_CONST(""),
940  int vendorCode =0)
941  :SQLException(reason,sqlState,vendorCode) {}
942 
945  :SQLException(dm) {}
946 
948  virtual ~SQLWarning() throw() {}
949  };
950 
951  typedef CleanVector<SQLWarning*> WarningList;
952 
953 
954  template <class T> class Deleter {
955  private:
956  T* ptr_;
957  bool isArray_;
958 
959  Deleter(const Deleter<T>&);
960  Deleter<T>& operator=(const Deleter<T>&);
961 
962  public:
963  explicit Deleter(T* ptr, bool isArray =false)
964  :ptr_(ptr), isArray_(isArray) {}
965  ~Deleter() {
966  if(!isArray_) {
967  ODBCXX_OPERATOR_DELETE_DEBUG(__FILE__, __LINE__) ptr_;
968  } else {
969  delete[] ptr_;
970  }
971  }
972  };
973 
974 } // namespace odbc
975 
976 
977 #endif // __ODBCXX_TYPES_H

Go back to the libodbc++ homepage