2002-07-13 09:13:02 +04:00
/*
Unix SMB / CIFS implementation .
SMB client library implementation ( Old interface compatibility )
Copyright ( C ) Andrew Tridgell 1998
Copyright ( C ) Richard Sharpe 2000
Copyright ( C ) John Terpstra 2000
Copyright ( C ) Tom Jansen ( Ninja ISD ) 2002
2008-02-28 19:23:20 +03:00
Copyright ( C ) Derrell Lipman 2003 , 2008
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2002-07-13 09:13:02 +04:00
( at your option ) any later version .
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
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 .
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2002-07-13 09:13:02 +04:00
*/
# include "includes.h"
2008-02-28 19:23:20 +03:00
# include "libsmb_internal.h"
2002-07-13 09:13:02 +04:00
struct smbc_compat_fdlist {
SMBCFILE * file ;
int fd ;
struct smbc_compat_fdlist * next , * prev ;
} ;
static SMBCCTX * statcont = NULL ;
static int smbc_compat_initialized = 0 ;
2003-10-24 21:01:19 +04:00
static int smbc_compat_nextfd = 0 ;
static struct smbc_compat_fdlist * smbc_compat_fd_in_use = NULL ;
static struct smbc_compat_fdlist * smbc_compat_fd_avail = NULL ;
2002-07-13 09:13:02 +04:00
/* Find an fd and return the SMBCFILE * or NULL on failure */
2008-02-28 19:23:20 +03:00
static SMBCFILE *
find_fd ( int fd )
2002-07-13 09:13:02 +04:00
{
2003-10-24 21:01:19 +04:00
struct smbc_compat_fdlist * f = smbc_compat_fd_in_use ;
2002-07-13 09:13:02 +04:00
while ( f ) {
if ( f - > fd = = fd )
return f - > file ;
f = f - > next ;
}
return NULL ;
}
/* Add an fd, returns 0 on success, -1 on error with errno set */
2008-02-28 19:23:20 +03:00
static int
add_fd ( SMBCFILE * file )
2002-07-13 09:13:02 +04:00
{
2003-10-24 21:01:19 +04:00
struct smbc_compat_fdlist * f = smbc_compat_fd_avail ;
2009-11-22 00:52:12 +03:00
2003-10-24 21:01:19 +04:00
if ( f ) {
/* We found one that's available */
DLIST_REMOVE ( smbc_compat_fd_avail , f ) ;
} else {
/*
* None were available , so allocate one . Keep the number of
* file descriptors determinate . This allows the application
* to allocate bitmaps or mapping of file descriptors based on
* a known maximum number of file descriptors that will ever
* be returned .
*/
if ( smbc_compat_nextfd > = FD_SETSIZE ) {
errno = EMFILE ;
return - 1 ;
}
2009-11-22 00:52:12 +03:00
2004-12-07 21:25:53 +03:00
f = SMB_MALLOC_P ( struct smbc_compat_fdlist ) ;
2003-10-24 21:01:19 +04:00
if ( ! f ) {
errno = ENOMEM ;
return - 1 ;
}
2009-11-22 00:52:12 +03:00
2003-10-24 21:01:19 +04:00
f - > fd = SMBC_BASE_FD + smbc_compat_nextfd + + ;
}
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
f - > file = file ;
2003-10-24 21:01:19 +04:00
DLIST_ADD ( smbc_compat_fd_in_use , f ) ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
return f - > fd ;
}
/* Delete an fd, returns 0 on success */
2008-02-28 19:23:20 +03:00
static int
del_fd ( int fd )
2002-07-13 09:13:02 +04:00
{
2003-10-24 21:01:19 +04:00
struct smbc_compat_fdlist * f = smbc_compat_fd_in_use ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
while ( f ) {
if ( f - > fd = = fd )
break ;
f = f - > next ;
}
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
if ( f ) {
/* found */
2003-10-24 21:01:19 +04:00
DLIST_REMOVE ( smbc_compat_fd_in_use , f ) ;
f - > file = NULL ;
DLIST_ADD ( smbc_compat_fd_avail , f ) ;
2002-07-13 09:13:02 +04:00
return 0 ;
}
return 1 ;
}
2008-03-02 04:44:21 +03:00
2002-07-13 09:13:02 +04:00
2008-02-28 19:23:20 +03:00
int
smbc_init ( smbc_get_auth_data_fn fn ,
int debug )
2002-07-13 09:13:02 +04:00
{
if ( ! smbc_compat_initialized ) {
statcont = smbc_new_context ( ) ;
if ( ! statcont )
return - 1 ;
2009-11-22 00:52:12 +03:00
2008-02-28 19:23:20 +03:00
smbc_setDebug ( statcont , debug ) ;
smbc_setFunctionAuthData ( statcont , fn ) ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
if ( ! smbc_init_context ( statcont ) ) {
smbc_free_context ( statcont , False ) ;
return - 1 ;
}
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
smbc_compat_initialized = 1 ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
return 0 ;
}
return 0 ;
}
2008-02-28 19:23:20 +03:00
SMBCCTX *
smbc_set_context ( SMBCCTX * context )
2003-10-24 21:01:19 +04:00
{
SMBCCTX * old_context = statcont ;
2009-11-22 00:52:12 +03:00
2003-10-24 21:01:19 +04:00
if ( context ) {
/* Save provided context. It must have been initialized! */
statcont = context ;
2009-11-22 00:52:12 +03:00
2003-10-24 21:01:19 +04:00
/* You'd better know what you're doing. We won't help you. */
smbc_compat_initialized = 1 ;
}
2009-11-22 00:52:12 +03:00
2003-10-24 21:01:19 +04:00
return old_context ;
}
2008-02-28 19:23:20 +03:00
int
smbc_open ( const char * furl ,
int flags ,
mode_t mode )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file ;
int fd ;
2009-11-22 00:52:12 +03:00
2008-02-28 19:23:20 +03:00
file = smbc_getFunctionOpen ( statcont ) ( statcont , furl , flags , mode ) ;
2002-07-13 09:13:02 +04:00
if ( ! file )
return - 1 ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
fd = add_fd ( file ) ;
if ( fd = = - 1 )
2008-02-28 19:23:20 +03:00
smbc_getFunctionClose ( statcont ) ( statcont , file ) ;
2002-07-13 09:13:02 +04:00
return fd ;
}
2008-02-28 19:23:20 +03:00
int
smbc_creat ( const char * furl ,
mode_t mode )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file ;
int fd ;
2009-11-22 00:52:12 +03:00
2008-02-28 19:23:20 +03:00
file = smbc_getFunctionCreat ( statcont ) ( statcont , furl , mode ) ;
2002-07-13 09:13:02 +04:00
if ( ! file )
return - 1 ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
fd = add_fd ( file ) ;
if ( fd = = - 1 ) {
/* Hmm... should we delete the file too ? I guess we could try */
2008-02-28 19:23:20 +03:00
smbc_getFunctionClose ( statcont ) ( statcont , file ) ;
smbc_getFunctionUnlink ( statcont ) ( statcont , furl ) ;
2002-07-13 09:13:02 +04:00
}
return fd ;
}
2008-02-28 19:23:20 +03:00
ssize_t
smbc_read ( int fd ,
void * buf ,
size_t bufsize )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionRead ( statcont ) ( statcont , file , buf , bufsize ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
ssize_t
smbc_write ( int fd ,
2008-07-16 18:45:09 +04:00
const void * buf ,
2008-02-28 19:23:20 +03:00
size_t bufsize )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionWrite ( statcont ) ( statcont , file , buf , bufsize ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
off_t
smbc_lseek ( int fd ,
off_t offset ,
int whence )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionLseek ( statcont ) ( statcont , file , offset , whence ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_close ( int fd )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
del_fd ( fd ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionClose ( statcont ) ( statcont , file ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_unlink ( const char * fname )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionUnlink ( statcont ) ( statcont , fname ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_rename ( const char * ourl ,
const char * nurl )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionRename ( statcont ) ( statcont , ourl ,
statcont , nurl ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_opendir ( const char * durl )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file ;
int fd ;
2009-11-22 00:52:12 +03:00
2008-02-28 19:23:20 +03:00
file = smbc_getFunctionOpendir ( statcont ) ( statcont , durl ) ;
2002-07-13 09:13:02 +04:00
if ( ! file )
return - 1 ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
fd = add_fd ( file ) ;
if ( fd = = - 1 )
2008-02-28 19:23:20 +03:00
smbc_getFunctionClosedir ( statcont ) ( statcont , file ) ;
2009-11-22 00:52:12 +03:00
2002-07-13 09:13:02 +04:00
return fd ;
}
2008-02-28 19:23:20 +03:00
int
smbc_closedir ( int dh )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( dh ) ;
del_fd ( dh ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionClosedir ( statcont ) ( statcont , file ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_getdents ( unsigned int dh ,
struct smbc_dirent * dirp ,
int count )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( dh ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionGetdents ( statcont ) ( statcont , file , dirp , count ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
struct smbc_dirent *
smbc_readdir ( unsigned int dh )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( dh ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionReaddir ( statcont ) ( statcont , file ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
off_t
smbc_telldir ( int dh )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( dh ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionTelldir ( statcont ) ( statcont , file ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_lseekdir ( int fd ,
off_t offset )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionLseekdir ( statcont ) ( statcont , file , offset ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_mkdir ( const char * durl ,
mode_t mode )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionMkdir ( statcont ) ( statcont , durl , mode ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_rmdir ( const char * durl )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionRmdir ( statcont ) ( statcont , durl ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_stat ( const char * url ,
struct stat * st )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionStat ( statcont ) ( statcont , url , st ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_fstat ( int fd ,
struct stat * st )
2002-07-13 09:13:02 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionFstat ( statcont ) ( statcont , file , st ) ;
2002-07-13 09:13:02 +04:00
}
2009-02-12 18:39:17 +03:00
int
smbc_statvfs ( char * path ,
2009-02-14 00:47:54 +03:00
struct statvfs * st )
2009-02-12 18:39:17 +03:00
{
return smbc_getFunctionStatVFS ( statcont ) ( statcont , path , st ) ;
}
int
smbc_fstatvfs ( int fd ,
2009-02-14 00:47:54 +03:00
struct statvfs * st )
2009-02-12 18:39:17 +03:00
{
SMBCFILE * file = find_fd ( fd ) ;
return smbc_getFunctionFstatVFS ( statcont ) ( statcont , file , st ) ;
}
2008-02-28 19:23:20 +03:00
int
smbc_ftruncate ( int fd ,
off_t size )
2008-02-27 05:44:51 +03:00
{
SMBCFILE * file = find_fd ( fd ) ;
2008-02-28 19:23:20 +03:00
return smbc_getFunctionFtruncate ( statcont ) ( statcont , file , size ) ;
2008-02-27 05:44:51 +03:00
}
2008-02-28 19:23:20 +03:00
int
smbc_chmod ( const char * url ,
mode_t mode )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionChmod ( statcont ) ( statcont , url , mode ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_utimes ( const char * fname ,
struct timeval * tbuf )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionUtimes ( statcont ) ( statcont , fname , tbuf ) ;
2003-10-24 21:01:19 +04:00
}
# ifdef HAVE_UTIME_H
2008-02-28 19:23:20 +03:00
int
smbc_utime ( const char * fname ,
struct utimbuf * utbuf )
2003-10-24 21:01:19 +04:00
{
2005-06-01 21:40:40 +04:00
struct timeval tv [ 2 ] ;
2009-11-22 00:52:12 +03:00
2003-10-24 21:01:19 +04:00
if ( utbuf = = NULL )
2008-02-28 19:23:20 +03:00
return smbc_getFunctionUtimes ( statcont ) ( statcont , fname , NULL ) ;
2009-11-22 00:52:12 +03:00
2005-06-01 21:40:40 +04:00
tv [ 0 ] . tv_sec = utbuf - > actime ;
tv [ 1 ] . tv_sec = utbuf - > modtime ;
tv [ 0 ] . tv_usec = tv [ 1 ] . tv_usec = 0 ;
2009-11-22 00:52:12 +03:00
2008-02-28 19:23:20 +03:00
return smbc_getFunctionUtimes ( statcont ) ( statcont , fname , tv ) ;
2003-10-24 21:01:19 +04:00
}
# endif
2008-02-28 19:23:20 +03:00
int
smbc_setxattr ( const char * fname ,
const char * name ,
const void * value ,
size_t size ,
int flags )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionSetxattr ( statcont ) ( statcont ,
fname , name ,
value , size , flags ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_lsetxattr ( const char * fname ,
const char * name ,
const void * value ,
size_t size ,
int flags )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionSetxattr ( statcont ) ( statcont ,
fname , name ,
value , size , flags ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_fsetxattr ( int fd ,
const char * name ,
const void * value ,
size_t size ,
int flags )
2003-10-24 21:01:19 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2006-06-19 23:07:39 +04:00
if ( file = = NULL ) {
errno = EBADF ;
return - 1 ;
}
2008-02-28 19:23:20 +03:00
return smbc_getFunctionSetxattr ( statcont ) ( statcont ,
file - > fname , name ,
value , size , flags ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_getxattr ( const char * fname ,
const char * name ,
const void * value ,
size_t size )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionGetxattr ( statcont ) ( statcont ,
fname , name ,
value , size ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_lgetxattr ( const char * fname ,
const char * name ,
const void * value ,
size_t size )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionGetxattr ( statcont ) ( statcont ,
fname , name ,
value , size ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_fgetxattr ( int fd ,
const char * name ,
const void * value ,
size_t size )
2003-10-24 21:01:19 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2006-06-19 23:07:39 +04:00
if ( file = = NULL ) {
errno = EBADF ;
return - 1 ;
}
2008-02-28 19:23:20 +03:00
return smbc_getFunctionGetxattr ( statcont ) ( statcont ,
file - > fname , name ,
value , size ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_removexattr ( const char * fname ,
const char * name )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionRemovexattr ( statcont ) ( statcont , fname , name ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_lremovexattr ( const char * fname ,
const char * name )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionRemovexattr ( statcont ) ( statcont , fname , name ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_fremovexattr ( int fd ,
const char * name )
2003-10-24 21:01:19 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2006-06-19 23:07:39 +04:00
if ( file = = NULL ) {
errno = EBADF ;
return - 1 ;
}
2008-02-28 19:23:20 +03:00
return smbc_getFunctionRemovexattr ( statcont ) ( statcont ,
file - > fname , name ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_listxattr ( const char * fname ,
char * list ,
size_t size )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionListxattr ( statcont ) ( statcont ,
fname , list , size ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_llistxattr ( const char * fname ,
char * list ,
size_t size )
2003-10-24 21:01:19 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionListxattr ( statcont ) ( statcont ,
fname , list , size ) ;
2003-10-24 21:01:19 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_flistxattr ( int fd ,
char * list ,
size_t size )
2003-10-24 21:01:19 +04:00
{
SMBCFILE * file = find_fd ( fd ) ;
2006-06-19 23:07:39 +04:00
if ( file = = NULL ) {
errno = EBADF ;
return - 1 ;
}
2008-02-28 19:23:20 +03:00
return smbc_getFunctionListxattr ( statcont ) ( statcont ,
file - > fname , list , size ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_print_file ( const char * fname ,
const char * printq )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionPrintFile ( statcont ) ( statcont , fname ,
statcont , printq ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_open_print_job ( const char * fname )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
SMBCFILE * file ;
2009-11-22 00:52:12 +03:00
2008-02-28 19:23:20 +03:00
file = smbc_getFunctionOpenPrintJob ( statcont ) ( statcont , fname ) ;
2002-07-13 09:13:02 +04:00
if ( ! file ) return - 1 ;
2005-09-30 21:13:37 +04:00
return file - > cli_fd ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_list_print_jobs ( const char * purl ,
smbc_list_print_job_fn fn )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionListPrintJobs ( statcont ) ( statcont , purl , fn ) ;
2002-07-13 09:13:02 +04:00
}
2008-02-28 19:23:20 +03:00
int
smbc_unlink_print_job ( const char * purl ,
int id )
2002-07-13 09:13:02 +04:00
{
2008-02-28 19:23:20 +03:00
return smbc_getFunctionUnlinkPrintJob ( statcont ) ( statcont , purl , id ) ;
2002-07-13 09:13:02 +04:00
}