Commit a7c5b9c2 authored by monty@donna.mysql.com's avatar monty@donna.mysql.com

Portability fixes for OPENBSD, OS2 and Windows

parent 5218a03a
......@@ -2222,6 +2222,9 @@ Apart from the following links, you can find and download a lot of
@item @uref{http://www.4t2.com/mysql}@*
Information about the German MySQL mailing list.
@item @uref{http://www2.rent-a-database.de/mysql/}
@strong{MySQL} manual in German.
@item @uref{http://www.bitmover.com:8888//home/bk/mysql}@*
Web access to the @strong{MySQL} BitKeeper repository.
......@@ -3612,7 +3615,7 @@ Payment should be made to:
Postgirot Bank AB
105 06 STOCKHOLM, SWEDEN
TCX DataKonsult AB
MySQL AB
BOX 6434
11382 STOCKHOLM, SWEDEN
......@@ -3629,7 +3632,7 @@ If you want to pay by check, make it payable to ``MySQL Finland AB'' and
mail it to the address below:
@example
TCX DataKonsult AB
MySQL AB
BOX 6434, Torsgatan 21
11382 STOCKHOLM, SWEDEN
@end example
......@@ -8903,17 +8906,19 @@ below:
@example
shell> BINDIR/mysqladmin version
mysqladmin Ver 6.3 Distrib 3.22.9-beta, for pc-linux-gnu on i686
TCX Datakonsult AB, by Monty
mysqladmin Ver 8.14 Distrib 3.23.32, for linux on i586
Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL license
Server version 3.22.9-beta
Server version 3.23.32-debug
Protocol version 10
Connection Localhost via Unix socket
TCP port 3306
UNIX socket /tmp/mysql.sock
Uptime: 16 sec
Running threads: 1 Questions: 20 Reloads: 2 Open tables: 3
Threads: 1 Questions: 9 Slow queries: 0 Opens: 7 Flush tables: 2 Open tables: 0 Queries per second avg: 0.000 Memory in use: 132K Max memory used: 16773K
@end example
To get a feeling for what else you can do with @code{BINDIR/mysqladmin},
......@@ -18528,7 +18533,7 @@ CHECK TABLE tbl_name[,tbl_name...] [option [option...]]
option = QUICK | FAST | MEDIUM | EXTEND | CHANGED
@end example
@code{CHECK TABLE} only works on @code{MyISAM} and @code{BDB} tables. On
@code{CHECK TABLE} only works on @code{MyISAM} tables. On
@code{MyISAM} tables it's the same thing as running @code{myisamchk -m
table_name} on the table.
......@@ -18567,9 +18572,6 @@ For dynamic sized @code{MyISAM} tables a started check will always
do a @code{MEDIUM} check. For static size rows we skip the row scan
for @code{QUICK} and @code{FAST} as the rows are very seldom corrupted.
Note that for BDB tables the different check options doesn't affect the
check in any way!
You can combine check options as in:
@example
......@@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script.
AC_INIT(sql/mysqld.cc)
AC_CANONICAL_SYSTEM
# The Docs Makefile.am parses this line!
AM_INIT_AUTOMAKE(mysql, 3.23.32)
AM_INIT_AUTOMAKE(mysql, 3.23.33)
AM_CONFIG_HEADER(config.h)
PROTOCOL_VERSION=10
......@@ -129,6 +129,7 @@ fi
# Still need ranlib for readline; local static use only so no libtool.
AC_PROG_RANLIB
# We use libtool
#AC_LIBTOOL_WIN32_DLL
AM_PROG_LIBTOOL
#AC_LIBTOOL_DLOPEN AC_LIBTOOL_WIN32_DLL AC_DISABLE_FAST_INSTALL AC_DISABLE_SHARED AC_DISABLE_STATIC
......
......@@ -41,13 +41,6 @@ extern int NEAR my_errno; /* Last error in mysys */
#include <stdarg.h>
#ifdef __EMX__
/* record loging flags (F_GETLK, F_SETLK, F_SETLKW) */
#define F_RDLCK 1 /* FreeBSD: shared or read lock */
#define F_UNLCK 2 /* FreeBSD: unlock */
#define F_WRLCK 3 /* FreeBSD: exclusive or write lock */
#endif
#define MYSYS_PROGRAM_USES_CURSES() { error_handler_hook = my_message_curses; mysys_uses_curses=1; }
#define MYSYS_PROGRAM_DONT_USE_CURSES() { error_handler_hook = my_message_no_curses; mysys_uses_curses=0;}
#define MY_INIT(name); { my_progname= name; my_init(); }
......
......@@ -1411,8 +1411,8 @@ else
os2*)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
allow_undefined_flag=unsupported
archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $objdir/$libname.def~$echo DATA >> $objdir/$libname.def~$echo " SINGLE NONSHARED" >> $objdir/$libname.def~$echo EXPORTS >> $objdir/$libname.def~emxexp $libobjs >> $objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $linkopts $objdir/$libname.def'
allow_undefined_flag=" "
archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $objdir/$libname.def~$echo DATA >> $objdir/$libname.def~$echo " SINGLE NONSHARED" >> $objdir/$libname.def~$echo EXPORTS >> $objdir/$libname.def~emxexp $libobjs >> $objdir/$libname.def~$CC -Zdll $linkflags -o $lib $libobjs $deplibs $objdir/$libname.def'
old_archive_from_new_cmds='emximp -o $objdir/$libname.a $objdir/$libname.def'
;;
......@@ -2036,6 +2036,7 @@ os2*)
need_lib_prefix=no
library_names_spec='$libname.dll $libname.a'
dynamic_linker='OS/2 ld.exe'
deplibs_check_method=pass_all
shlibpath_var=LIBPATH
;;
......
......@@ -819,6 +819,7 @@ compiler."
old_convenience=
deplibs=
linkopts=
linkflags=
if test -n "$shlibpath_var"; then
# get the directories listed in $shlibpath_var
......@@ -1159,6 +1160,7 @@ compiler."
# Unknown arguments in both finalize_command and compile_command need
# to be aesthetically quoted because they are evaled later.
arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
linkflags="$linkflags $arg"
case "$arg" in
*[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*)
arg="\"$arg\""
......
......@@ -44,6 +44,7 @@ static char *rcsid = "$Id$";
#include <resolv.h>
#define NET_BUFSIZE 4096
#undef ALIGN
#define ALIGN(p, t) ((char *)(((((long)(p) - 1) / sizeof(t)) + 1) * sizeof(t)))
#define SP(p, t, n) (ALIGN(p, t) + (n) * sizeof(t))
......
......@@ -44,6 +44,7 @@ static char *rcsid = "$Id$";
#include <resolv.h>
#define PROTO_BUFSIZE 4096
#undef ALIGN
#define ALIGN(p, t) ((char *)(((((long)(p) - 1) / sizeof(t)) + 1) * sizeof(t)))
#define SP(p, t, n) (ALIGN(p, t) + (n) * sizeof(t))
......
......@@ -44,6 +44,7 @@ static char *rcsid = "$Id$";
#include <resolv.h>
#define HOST_BUFSIZE 4096
#undef ALIGN
#define ALIGN(p, t) ((char *)(((((long)(p) - 1) / sizeof(t)) + 1) * sizeof(t)))
#define SP(p, t, n) (ALIGN(p, t) + (n) * sizeof(t))
......
......@@ -44,6 +44,7 @@ static char *rcsid = "$Id$";
#include <resolv.h>
#define SERV_BUFSIZE 4096
#undef ALIGN
#define ALIGN(p, t) ((char *)(((((long)(p) - 1) / sizeof(t)) + 1) * sizeof(t)))
#define SP(p, t, n) (ALIGN(p, t) + (n) * sizeof(t))
......
......@@ -36,11 +36,12 @@ int mode;
int handle_locking;
{
int save_errno,i,errpos;
uint files,dir_length;
uint files,dir_length,length;
ulonglong file_offset;
char name_buff[FN_REFLEN*2],buff[FN_REFLEN],*end;
MYRG_INFO info,*m_info;
FILE *file;
File fd;
IO_CACHE file;
MI_INFO *isam,*last_isam;
DBUG_ENTER("myrg_open");
......@@ -49,15 +50,18 @@ int handle_locking;
isam=0;
errpos=files=0;
bzero((gptr) &info,sizeof(info));
if (!(file=my_fopen(fn_format(name_buff,name,"",MYRG_NAME_EXT,4),
O_RDONLY | O_SHARE,MYF(0))))
bzero((char*) &file,sizeof(file));
if ((fd=my_open(fn_format(name_buff,name,"",MYRG_NAME_EXT,4),
O_RDONLY | O_SHARE,MYF(0))) < 0 ||
init_io_cache(&file, fd, IO_SIZE, READ_CACHE, 0, 0,
MYF(MY_WME | MY_NABP)))
goto err;
errpos=1;
dir_length=dirname_part(name_buff,name);
info.reclength=0;
while (fgets(buff,FN_REFLEN-1,file))
while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
{
if ((end=strend(buff))[-1] == '\n')
if ((end=buff+length)[-1] == '\n')
end[-1]='\0';
if (buff[0] && buff[0] != '#') /* Skipp empty lines and comments */
{
......@@ -118,7 +122,8 @@ int handle_locking;
m_info->end_table=m_info->open_tables+files;
m_info->last_used_table=m_info->open_tables;
VOID(my_fclose(file,MYF(0)));
VOID(my_close(fd,MYF(0)));
end_io_cache(&file);
m_info->open_list.data=(void*) m_info;
pthread_mutex_lock(&THR_LOCK_open);
myrg_open_list=list_add(myrg_open_list,&m_info->open_list);
......@@ -132,7 +137,8 @@ err:
my_free((char*) m_info,MYF(0));
/* Fall through */
case 1:
VOID(my_fclose(file,MYF(0)));
VOID(my_close(fd,MYF(0)));
end_io_cache(&file);
for (i=files ; i-- > 0 ; )
{
isam=last_isam;
......
......@@ -29,6 +29,10 @@
#include <direct.h>
#endif
#ifdef __EMX__
// chdir2 support also drive change
#define chdir _chdir2
#endif
/* Gets current working directory in buff. Directory is allways ended
with FN_LIBCHAR */
......
......@@ -4710,13 +4710,13 @@ create_field::create_field(Field *old_field,Field *orig_field)
orig_field)
{
char buff[MAX_FIELD_WIDTH],*pos;
String tmp(buff,sizeof(buff)),*res;
String tmp(buff,sizeof(buff));
/* Get the value from record[2] (the default value row) */
my_ptrdiff_t diff= (my_ptrdiff_t) (orig_field->table->rec_buff_length*2);
orig_field->move_field(diff); // Points now at record[2]
bool is_null=orig_field->is_real_null();
res=orig_field->val_str(&tmp,&tmp);
orig_field->val_str(&tmp,&tmp);
orig_field->move_field(-diff); // Back to record[0]
if (!is_null)
{
......
......@@ -28,6 +28,7 @@
class Send_field;
struct st_cache_field;
void field_conv(Field *to,Field *from);
class Field {
Field(const Item &); /* Prevent use of theese */
......
......@@ -110,8 +110,6 @@ static void berkeley_noticecall(DB_ENV *db_env, db_notices notice);
bool berkeley_init(void)
{
char buff[1024],*config[10], **conf_pos, *str_pos;
conf_pos=config; str_pos=buff;
DBUG_ENTER("berkeley_init");
if (!berkeley_tmpdir)
......@@ -387,6 +385,7 @@ berkeley_cmp_packed_key(DB *file, const DBT *new_key, const DBT *saved_key)
/* The following is not yet used; Should be used for fixed length keys */
#ifdef NOT_YET
static int
berkeley_cmp_fix_length_key(DB *file, const DBT *new_key, const DBT *saved_key)
{
......@@ -407,7 +406,7 @@ berkeley_cmp_fix_length_key(DB *file, const DBT *new_key, const DBT *saved_key)
}
return key->handler.bdb_return_if_eq;
}
#endif
/* Compare key against row */
......@@ -1901,7 +1900,6 @@ longlong ha_berkeley::get_auto_increment()
{
DBT row,old_key;
bzero((char*) &row,sizeof(row));
uint key_len;
KEY *key_info= &table->key_info[active_index];
/* Reading next available number for a sub key */
......@@ -1947,6 +1945,7 @@ longlong ha_berkeley::get_auto_increment()
Analyzing, checking, and optimizing tables
****************************************************************************/
#ifdef NOT_YET
static void print_msg(THD *thd, const char *table_name, const char *op_name,
const char *msg_type, const char *fmt, ...)
{
......@@ -1970,7 +1969,7 @@ static void print_msg(THD *thd, const char *table_name, const char *op_name,
thd->packet.length()))
thd->killed=1;
}
#endif
int ha_berkeley::analyze(THD* thd, HA_CHECK_OPT* check_opt)
{
......@@ -2025,14 +2024,14 @@ int ha_berkeley::optimize(THD* thd, HA_CHECK_OPT* check_opt)
int ha_berkeley::check(THD* thd, HA_CHECK_OPT* check_opt)
{
char name_buff[FN_REFLEN];
int error;
DB *tmp_file;
DBUG_ENTER("ha_berkeley::check");
DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
#ifdef NOT_YET
char name_buff[FN_REFLEN];
int error;
DB *tmp_file;
/*
To get this to work we need to ensure that no running transaction is
using the table. We also need to create a new environment without
......@@ -2242,7 +2241,6 @@ void ha_berkeley::get_status()
static int write_status(DB *status_block, char *buff, uint length)
{
DB_TXN *trans;
DBT row,key;
int error;
const char *key_buff="status";
......@@ -2285,7 +2283,6 @@ static void update_status(BDB_SHARE *share, TABLE *table)
}
{
char rec_buff[4+MAX_KEY*4], *pos=rec_buff;
const char *key_buff="status";
int4store(pos,share->rows); pos+=4;
for (uint i=0 ; i < table->keys ; i++)
{
......
......@@ -64,7 +64,7 @@ class ha_berkeley: public handler
ulong max_row_length(const byte *buf);
int pack_row(DBT *row,const byte *record, bool new_row);
void unpack_row(char *record, DBT *row);
void ha_berkeley::unpack_key(char *record, DBT *key, uint index);
void unpack_key(char *record, DBT *key, uint index);
DBT *create_key(DBT *key, uint keynr, char *buff, const byte *record,
int key_length = MAX_KEY_LENGTH);
DBT *pack_key(DBT *key, uint keynr, char *buff, const byte *key_ptr,
......@@ -156,13 +156,12 @@ class ha_berkeley: public handler
void get_status();
inline void get_auto_primary_key(byte *to)
{
ulonglong tmp;
pthread_mutex_lock(&share->mutex);
share->auto_ident++;
int5store(to,share->auto_ident);
pthread_mutex_unlock(&share->mutex);
}
longlong ha_berkeley::get_auto_increment();
longlong get_auto_increment();
};
extern bool berkeley_skip, berkeley_shared_data;
......
......@@ -166,6 +166,8 @@ class Item_sum_count_distinct :public Item_sum_int
/* Item to get the value of a stored sum function */
class Item_sum_avg;
class Item_avg_field :public Item_result_field
{
public:
......@@ -200,6 +202,7 @@ class Item_sum_avg :public Item_sum_num
const char *func_name() const { return "avg"; }
};
class Item_sum_std;
class Item_std_field :public Item_result_field
{
......
......@@ -18,6 +18,10 @@
/* logging of commands */
/* TODO: Abort logging when we get an error in reading or writing log files */
#ifdef __EMX__
#include <io.h>
#endif
#include "mysql_priv.h"
#include "sql_acl.h"
#include "sql_repl.h"
......
......@@ -18,6 +18,10 @@
#ifndef _LOG_EVENT_H
#define _LOG_EVENT_H
#ifdef __EMX__
#undef write // remove pthread.h macro definition, conflict with write() class member
#endif
#if defined(__GNUC__) && !defined(MYSQL_CLIENT)
#pragma interface /* gcc class implementation */
#endif
......
......@@ -1048,7 +1048,6 @@ static void init_signals(void)
#elif defined(__EMX__)
static void sig_reload(int signo)
{
//reload_acl_and_cache(~0); // Flush everything
reload_acl_and_cache((THD*) 0,~0, (TABLE_LIST*) 0); // Flush everything
signal(signo, SIG_ACK);
}
......
......@@ -271,7 +271,7 @@ net_real_write(NET *net,const char *packet,ulong len)
int length;
char *pos,*end;
thr_alarm_t alarmed;
#if !defined(__WIN__) && !defined(__EMX__)
#if !defined(__WIN__)
ALARM alarm_buff;
#endif
uint retry_count=0;
......@@ -426,7 +426,7 @@ static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed)
if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L)
{
my_bool interrupted = vio_should_retry(net->vio);
if (!thr_got_alarm(&alarmed) && interrupted)
if (!thr_got_alarm(alarmed) && interrupted)
{ /* Probably in MIT threads */
if (retry_count++ < RETRY_COUNT)
continue;
......
......@@ -88,6 +88,9 @@ public:
};
int collect_string(String *element, element_count count,
TREE_INFO *info);
class field_str :public field_info
{
String min_arg, max_arg;
......@@ -130,6 +133,9 @@ public:
{ return (String*) 0; }
};
int collect_real(double *element, element_count count, TREE_INFO *info);
class field_real: public field_info
{
double min_arg, max_arg;
......@@ -174,6 +180,9 @@ public:
{ return (tree_walk_action) collect_real;}
};
int collect_longlong(longlong *element, element_count count,
TREE_INFO *info);
class field_longlong: public field_info
{
longlong min_arg, max_arg;
......@@ -216,6 +225,8 @@ public:
{ return (tree_walk_action) collect_longlong;}
};
int collect_ulonglong(ulonglong *element, element_count count,
TREE_INFO *info);
class field_ulonglong: public field_info
{
......@@ -261,6 +272,10 @@ public:
};
Procedure *proc_analyse_init(THD *thd, ORDER *param,
select_result *result,
List<Item> &field_list);
class analyse: public Procedure
{
protected:
......
......@@ -102,6 +102,9 @@ public:
/* character conversion tables */
class CONVERT;
CONVERT *get_convert_set(const char *name_ptr);
class CONVERT
{
const uchar *from_map,*to_map;
......
......@@ -21,6 +21,10 @@
#pragma interface /* gcc class implementation */
#endif
class mapped_files;
mapped_files *map_file(const my_string name,byte *magic,uint magic_length);
void unmap_file(mapped_files *map);
class mapped_files :public ilink {
byte *map;
ha_rows size;
......
......@@ -26,8 +26,7 @@
extern const char* any_db;
extern pthread_handler_decl(handle_slave,arg);
static int fake_rotate_event(NET* net, String* packet,
const char* log_file_name);
static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
const char**errmsg)
......
......@@ -25,6 +25,13 @@
#define NOT_FIXED_DEC 31
#endif
class String;
int sortcmp(const String *a,const String *b);
int stringcmp(const String *a,const String *b);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
int wild_case_compare(String &match,String &wild,char escape);
int wild_compare(String &match,String &wild,char escape);
class String
{
char *Ptr;
......
......@@ -798,7 +798,6 @@ static int send_check_errmsg(THD* thd, TABLE_LIST* table,
static int prepare_for_restore(THD* thd, TABLE_LIST* table)
{
String *packet = &thd->packet;
DBUG_ENTER("prepare_for_restore");
if (table->table) // do not overwrite existing tables on restore
......
......@@ -14,8 +14,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Optimized longlong2str function for Intel 80x86 (gcc/gas syntax) */
/* Some set sequences are optimized for pentuimpro II */
# Optimized longlong2str function for Intel 80x86 (gcc/gas syntax)
# Some set sequences are optimized for pentuimpro II
.file "longlong2str.s"
.version "1.01"
......@@ -32,90 +32,90 @@ longlong2str:
pushl %esi
pushl %edi
pushl %ebx
movl 100(%esp),%esi /* Lower part of val */
movl 104(%esp),%ebp /* Higher part of val */
movl 108(%esp),%edi /* get dst */
movl 112(%esp),%ebx /* Radix */
movl 100(%esp),%esi # Lower part of val
movl 104(%esp),%ebp # Higher part of val
movl 108(%esp),%edi # get dst
movl 112(%esp),%ebx # Radix
movl %ebx,%eax
testl %eax,%eax
jge .L144
addl $36,%eax
cmpl $34,%eax
ja .Lerror /* Wrong radix */
ja .Lerror # Wrong radix
testl %ebp,%ebp
jge .L146
movb $45,(%edi) /* Add sign */
incl %edi /* Change sign of val */
movb $45,(%edi) # Add sign
incl %edi # Change sign of val
negl %esi
adcl $0,%ebp
negl %ebp
.L146:
negl %ebx /* Change radix to positive */
negl %ebx # Change radix to positive
jmp .L148
.p2align 4,,7
.align 4
.L144:
addl $-2,%eax
cmpl $34,%eax
ja .Lerror /* Radix in range */
ja .Lerror # Radix in range
.L148:
movl %esi,%eax /* Test if zero (for easy loop) */
movl %esi,%eax # Test if zero (for easy loop)
orl %ebp,%eax
jne .L150
movb $48,(%edi)
incl %edi
jmp .L164
.p2align 4,,7
.align 4
.L150:
leal 92(%esp),%ecx /* End of buffer */
leal 92(%esp),%ecx # End of buffer
jmp .L155
.p2align 4,,7
.align 4
.L153:
/* val is stored in in ebp:esi */
# val is stored in in ebp:esi
movl %ebp,%eax /* High part of value */
movl %ebp,%eax # High part of value
xorl %edx,%edx
divl %ebx
movl %eax,%ebp
movl %esi,%eax
divl %ebx
movl %eax,%esi /* quotent in ebp:esi */
movb _dig_vec(%edx),%al /* al is faster than dl */
movl %eax,%esi # quotent in ebp:esi
movb _dig_vec(%edx),%al # al is faster than dl
decl %ecx
movb %al,(%ecx) /* store value in buff */
.p2align 4,,7
movb %al,(%ecx) # store value in buff
.align 4
.L155:
testl %ebp,%ebp
ja .L153
testl %esi,%esi /* rest value */
testl %esi,%esi # rest value
jl .L153
je .L160 /* Ready */
je .L160 # Ready
movl %esi,%eax
movl $_dig_vec,%ebp
.p2align 4,,7
.align 4
.L154: /* Do rest with integer precision */
.L154: # Do rest with integer precision
cltd
divl %ebx
decl %ecx
movb (%edx,%ebp),%dl /* bh is always zero as ebx=radix < 36 */
movb (%edx,%ebp),%dl # bh is always zero as ebx=radix < 36
testl %eax,%eax
movb %dl,(%ecx)
jne .L154
.L160:
movl %ecx,%esi
leal 92(%esp),%ecx /* End of buffer */
leal 92(%esp),%ecx # End of buffer
subl %esi,%ecx
rep
movsb
.L164:
movl %edi,%eax /* Pointer to end null */
movb $0,(%edi) /* Store the end null */
movl %edi,%eax # Pointer to end null
movb $0,(%edi) # Store the end null
.L165:
popl %ebx
......@@ -126,7 +126,7 @@ longlong2str:
ret
.Lerror:
xorl %eax,%eax /* Wrong radix */
xorl %eax,%eax # Wrong radix
jmp .L165
.Lfe3:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment