/*	File		:	Socket.C
**	Author		:	Dr. Clue (A.K.A. Ian A. Storms)
**	Created		:	April	 12th ,1996
**	Last Update	:	December 26th ,2005
**	Description	:  A generic class for working with sockets.
**	This class is intended to make the use of sockets
**	in an application a trivial matter for the programmer.
**
**	This class provides both client and server
**	functionality. In the server mode, it can
**	be both forking and non-forking.
**
**	Hooks are available to allow programmer defined
**	reception routines.
*/
#include "F1Socket.h"

/*	Function	:	Dr_KavorkianSIGCHLD()
**	Parameters	:	...
**	Returns		:	void
**	Description	:	As children expire catch their returns
**		or else we get zombies. Dr_Kavorkian() catches
**		falling child processes and puts them out of their misery.
*/
void	Dr_KavorkianSIGCHLD(SIG_PARAM)       // this eliminates zombies   
	{ 
	fprintf(stderr,"*** Dr. KavorkianSIGCHLD "); 
	while (waitpid(-1, NULL, WNOHANG) > 0)fprintf(stderr,"\nKills Zombie" );
	signal(SIGCHLD, Dr_KavorkianSIGCHLD);            
	}
/*	Function	:	Dr_KavorkianSIGPIPE
**	Parameters	:	...
**	Returns		:	void
**	Description	:	This is supposed to indicate that the
**			socket connection ( Pipe ) has been broken and
**			is no longer available for communications.
*/
void    Dr_KavorkianSIGPIPE(SIG_PARAM)
        {
	fprintf(stderr,"*** Dr. KavorkianSIGPIPE "); 
	signal(SIGPIPE, Dr_KavorkianSIGPIPE );
        }
/*	Function	:	Dr_KavorkianSIGSEGV
**	Parameters	:	...
**	Returns		:	void
**	Description	:	Signal indicates that the process has
**			made a segmentation violation.	imslsp.dll
*/
void    Dr_KavorkianSIGSEGV(SIG_PARAM)
        {
	fprintf(stderr,"*** Dr. KavorkianSIGEGV "); 
//	exit(0);
	signal(SIGSEGV, Dr_KavorkianSIGSEGV );
        }
/*	Function	:	SockPrintBuffer()
**	Parameters	:	F1_pVoid	vpBUFFER
**			 	F1_int		iLEN
**	Returns		:	void
**	Description	:	The SocketPrintFunction is a default
**		printing function that directs it's output
**		to stderr. Typically one assigns the address
**		of their own printing routine to the member variable
**		Socket::RDF (Receive Data Function), which is used
**		to process the data in whatever fashion you desire.
*/
void Socket::SockPrintBuffer(	F1_pVoid	vpBUFFER	,
				F1_int		iLEN		,
				F1_pVoid	vState		)
	{
	zprintf("[SOK]%*.*s[/SOK]",iLEN,iLEN,vpBUFFER);return;
	}
/*	CONSTRUCTOR	:	Socket		-CONSTRUCTOR-
**	Parameters	:	None
**	Returns		:	None
**	Description	:
*/
Socket::Socket()
	{
	iEcho			= 0			;
	iSStatus		= SocketStatusOk	;
	iSHandle		=-1			;//Handle used in socket connection
	Hit_Cnt			= 0			;//Used with the Server mode to count hits
	Write_Total		= Read_Total	=0	;//Tracks the bytes transfered lifetime
	Write_Last		= Read_Last	=0	;//Tracks the bytes transfered lifetime
	RDF			= NULL			;//SockPrintBuffer;
	RHF			= NULL			;//No Fork Function;   
	
	//	Socket connection timming for initial connections
	iW_sec	=iR_sec		= SELECT_TIMEOUT	;//seconds	to wait (W)rite (R)ead
	iW_Msec	=iR_Msec	= 0			;//milliseconds to wait (W)rite (R)ead
	//	Socket connection timming for ongoing connections
	iSW_sec	=iSR_sec 	= 0			;//seconds	to wait (W)rite (R)ead
	iSW_Msec=iSW_Msec	= 0			;//milliseconds to wait (W)rite (R)ead
	//	
	TV_TIME.tv_sec  	= SELECT_TIMEOUT	;
	TV_TIME.tv_usec 	= 0			;
	//
	setHostname(dHOST_NAME)				;
	}
/*	DESTRUCTOR	:	~Socket()	-DESTRUCTOR-
**	Parameters	:	N/A
**	Returns		:	N/A
**	Description	:	Closes Socket (If open).
*/
Socket::~Socket()
	{
	if(iSHandle!=-1)CLOSE();
	}
/*	Function	:	COUNTS()
**	Parameters	:	N/A
**	Returns		:	F1_int
**	Description	:
*/
F1_int	Socket::COUNTS()
		{
		zprintf(" WRITE LAST=[%d] TOTAL=[%l] ",Write_Last,Write_Total);
		return Write_Last;
		}
/*	Function	:	SEND_FILE()
**	Parameters	:	F1_pChar filename
**	Description	:	Send the named file over the current socket connection.
**	Returns		:	F1_int
*/
F1_int	Socket::SEND_FILE(	F1_pChar filename)
	{
	FILE	*fd   =NULL;
	F1_char	 Tbuff [80];
	F1_int	 rdcnt     ;
	if((fd=fopen(filename,"r"))==NULL)
		{
		return 0;
		}else{
		while((rdcnt=fread(Tbuff,1,79,fd))>0)
			{
			WRITE(Tbuff,rdcnt);
//			Tbuff[rdcnt]=0;
//			(*this)<< Tbuff;
			};
		fclose(fd);
		return 1;
		};
	}
/*	Function	:	WRITE()
**	Parameters	:	F1_pVoid	Buffer
**			 	F1_int		len
**	Returns		:	F1_int
**	Description	:
*/
F1_int	Socket::WRITE(	F1_pVoid	szwBuffer	,
			F1_int		len		)
	{
	F1_int Rval=0;
	if(!IS_OPEN()	){return -1;}
	if(!IS_WRITABLE(iW_sec,iW_Msec))
		{
		zprintf("\n::WRITE(void *szwBuffer,int len) IS_WRITEABLE() Fails Life Sucks");
		CLOSE();
		return Write_Last=0;
		}
	if((Rval=write(HAND(),szwBuffer,len))<0	)
		perror("Socket::WRITE(void*,int len)[]");
	if(Rval>-1				)Write_Total+=Rval;
	if(iEcho				)fprintf(stdout,"%*.*s",len,len,szwBuffer);
	return Write_Last=Rval;
	}
/*	Function	:	operator<<()
**	Parameters	:	F1_pChar outstr
**	Returns		:	C_SOCKET&
**	Description	:
*/
F1_SOCKET &Socket::operator<<(F1_pChar szOutstr)
	{
	if(IS_OPEN())WRITE(szOutstr,strlen(szOutstr));
	return *this;
	}
/*	Function	:	operator<<()
**	Parameters	:	F1_STRING	outstr
**	Returns		:	C_SOCKET&
**	Description	:
*/
F1_SOCKET &Socket::operator<<(F1_STRING obOutstr)
	{
	if(IS_OPEN())WRITE(obOutstr(),obOutstr.length());
	return *this;
	}
/*	Function	:	operator<<()
**	Parameters	:	F1_char	outchar
**	Returns		:	C_SOCKET&
**	Description	:
*/
F1_SOCKET &Socket::operator<<(F1_char   outchar)
	{
	if(IS_OPEN())WRITE((F1_pVoid) &outchar ,1);
	return *this;
	}
/*	Function	:	READ()
**	Parameters	:	F1_pVoid	 Buffer		
**				F1_int		 Len
**				F1_int		 Delay_Seconds
**				F1_int		 Delay_MSeconds
**				F1_int		SDelay_Seconds
**				F1_int		SDelay_MSeconds
**	Returns		:	F1_int		Number of bytes read.
**	Description	:	Attempts to read data from the current connection
**			and will wait up to Delay_Seconds and Delay_MSeconds for
**			the first data to become available, then after the initial 
**			start of data, it will wait up to SDelay_Seconds and SDelay_MSeconds
**			for further data to become available.
*/
F1_int	Socket::READ(	F1_pVoid	 szrBuffer	,F1_int	 Len		,
			F1_int		 Delay_Seconds	,F1_int	 Delay_MSeconds	,
			F1_int		SDelay_Seconds	,F1_int	SDelay_MSeconds	)
	{
	F1_int  C       =0		,bytesread	=0		;

	F1_int	iR	= Delay_Seconds	,iRM		= Delay_MSeconds,
		iSR	=SDelay_Seconds	,iSRM		=SDelay_MSeconds;

	if(iR	<0)iR	=iR_sec		;
	if(iRM	<0)iRM	=iR_Msec	;
	if(iSR	<0)iR	=iSR_sec	;
	if(iSRM	<0)iRM	=iSR_Msec	;

	if(IS_READABLE(iR,iRM))
		{
		do	{
			if((C = read(HAND(),(void *)&((char *)szrBuffer	)[bytesread],80))<1)break;
			if( C > 0					)bytesread+=C;
			}while(	IS_READABLE(iSR,iSRM)			&&
				bytesread<Len				);
			((char*)szrBuffer)[bytesread]=0;
		}
	Read_Last   = bytesread; Read_Total += Read_Last; return bytesread;
	}
/*	Function	:RDF_PRINT()
**	Parameters	:F1_int	Delay_Seconds	-Initial wait seconds
**			 F1_int	Delay_MSeconds	-Initial wait mseconds
**			 F1_int SDelay_Seconds	-Stall	 wait seconds
**			 F1_int SDelay_MSeconds -Stall	 wait mseconds
**	Returns		:F1_int	Bytes read from socket.
**	Description	:The RDF stands for (Received Data Function).
**		RDF_PRINT() is called after a request has been sent,
**		so as to receive the response. The parameters involve
**		to pairs of timing values, which allow one delay
**		for the start of reception and a shorter delay
**		for an on-going reception of a stream.
**		If one has assigned a function to the RDF function
**		pointer, then the data will be sent to that function,
**		otherwise the inboard SocketPrintBuffer() function
**		will receive the data.
*/
#define RDF_BUFFER_SIZE 80
F1_int	Socket::RDF_PRINT(	F1_int  Delay_Seconds,F1_int  Delay_MSeconds	,
				F1_int SDelay_Seconds,F1_int SDelay_MSeconds	,
				F1_pVoid	vUserData			)
	{
	F1_bool	bIsReadable	=IS_READABLE(Delay_Seconds,Delay_MSeconds);
	F1_int	iBytes		,
		bytesread	=0	;
	F1_char BUFFER[RDF_BUFFER_SIZE]	;
	
//fprintf(stdout,"bIsReadable=%d",bIsReadable);fflush(stdout);

	if(bIsReadable)
		{

	do	{
		if((iBytes = read(HAND(),(void *) BUFFER,RDF_BUFFER_SIZE))<1	)break;
		if( iBytes > 0		)bytesread+=iBytes;
		if(RDF)	{			(*RDF)		(BUFFER,iBytes,vUserData);
			}else{			SockPrintBuffer	(BUFFER,iBytes,vUserData);};
		}while(	IS_READABLE(	SDelay_Seconds			,
					SDelay_MSeconds			));

		}//endif
	Read_Last = bytesread;Read_Total += Read_Last; return bytesread;
	}
/*	Function	:	CLOSE()
**	Parameters	:	None
**	Returns		:	F1_int
**	Description	:	Closes the socket if it's open
*/
F1_int	Socket::CLOSE()
	{
	if(iSHandle!=-1)close(iSHandle);
	iSHandle=-1;iSStatus=SocketStatusNoHandle;
	return(0);
	}
/*	Function	:SetStatus()
**	Parameters	:N/A
**	Returns		:F1_int
**	Description	:
*/
F1_int	Socket::SetStatus()
	{
	if(iSHandle==-1)return(iSStatus=SocketStatusNoHandle);
	switch(errno)
		{
	case EISCONN     :return iSStatus=SocketStatusAlreadyConnected  ;
	case ETIMEDOUT   :return iSStatus=SocketStatusTimedOut          ;
	case ECONNREFUSED:return iSStatus=SocketStatusConnectionRefused ;
	case ENETUNREACH :return iSStatus=SocketStatusNetworkUnreachable;
	default          :return iSStatus=SocketStatusFailed            ;
		};
	}
/*	Function	:	zprintf()
**	Parameters	:	const F1_pChar fmt
**				...
**	Returns		:	F1_pChar
**	Description	:
*/
F1_int	Socket::zprintf(const F1_pChar fmt, ...)
	{
	static F1_char Buffer[4255];
	va_list		ap		;
	va_start(	ap	,fmt	);
	(void) vsprintf(Buffer	,fmt, ap);
	va_end(		ap		);
	callbackSTATUS(Buffer);
	return 0;
	}
/*	Function	:	callbackSTATUS()
**	Parameters	:	void
**	Returns		:	F1_pChar
**	Description	:
*/
void	Socket::callbackSTATUS(F1_pChar szStatus)
	{
	fprintf(stderr,"%s\n",szStatus);	
	}
/*	Function	:	IP()
**	Parameters	:	void
**	Returns		:	F1_pChar
**	Description	:
*/
F1_pChar	Socket::IP()
	{
	F1_STRING	obIP						;
	obIP.stringf("%u.%u.%u.%u",
			255&	((F1_pChar)&sa_in.sin_addr)[0]		,
			255&	((F1_pChar)&sa_in.sin_addr)[1]		,
			255&	((F1_pChar)&sa_in.sin_addr)[2]		,
			255&	((F1_pChar)&sa_in.sin_addr)[3]		);
	return obIP.toString();
	}
/*	Function	:	CONNECT()
**	Parameters	:	F1_pChar	Hostname
**			 	F1_int		Portnumber
**	Returns		:	SocketStatus	SocketStatusHostNotFound
**						SocketStatusSocketFailed
**						SocketStatusConnectionFailed
**						SocketStatusOk
**	Description	:	Here we try to make a connection
**			to the server and port number provided.
*/
SocketStatus	Socket::CONNECT(	F1_pChar	Hostname	,
					F1_int		Portnumber	)
	{
        F1_pChar		tp				;
				obSHostname	=Hostname	;
				iSPortnumber	=Portnumber	;
	struct	hostent*	hp		=NULL		;

	//### HOST LOOKUP
	if(IS_OPEN())CLOSE();
#ifdef SHOW_SOCKET_PROGRESS
	zprintf("Looking Up\t: [%s] ",obSHostname());
#endif	
	if((hp=gethostbyname(obSHostname.toString()))==NULL)
		{
		zprintf("Error\t\t:Host not found"	,
			obSHostname.toString()		);
		return (iSStatus=SocketStatusHostNotFound);
		};
#ifdef SHOW_SOCKET_PROGRESS
	zprintf("Located Host\t: [%s] ",obSHostname());
#endif
	//### Socket Creation
 	bzero(&sa_in,sizeof(sa_in)	);
        bcopy(hp->h_addr,(F1_pChar)&sa_in.sin_addr,hp->h_length)	; /* set address */
	tp			=(F1_pChar)	&sa_in.sin_addr	;
	sa_in.sin_family	= hp->h_addrtype			; // should be AF_INET
	sa_in.sin_port		= htons((unsigned short)iSPortnumber)	;
#ifdef SHOW_SOCKET_PROGRESS
	zprintf("Creating Socket\t: [%s] [%s]",obSHostname(),IP());
#endif
	try	{
		signal(SIGPIPE	, Dr_KavorkianSIGSEGV	);		// This addresses Broken Pipes
		iSHandle		= socket(	hp->h_addrtype		, //hp->h_addrtype		,	//AF_INET		,
							SOCK_STREAM		, // SOCK_STREAM
							0			);//IPPROTO_TCP	); // 0 IPPROTO_TCP
		}
	catch(F1_int er)
		{
		switch(er)	{
		default		:	zprintf("\n *** SOCKET FAILS ***");
					perror("Socket::CONNECT() select error=");
				}//END SWITCH
		}
	if(!IS_OPEN())return (iSStatus=SocketStatusSocketFailed);

	//### Connect
	for(;;)
		{
		if (connect(				HAND()		,
				(struct sockaddr *)	&sa_in		,
				 sizeof sa_in				) <0)
			{
			if(errno==EINTR)continue;
			SetStatus();zprintf("*** No Connection *** \n");
			perror("Socket::CONNECT()" );
			return (iSStatus=SocketStatusConnectionFailed); //iSStatus;
			};
#ifdef SHOW_SOCKET_PROGRESS
		zprintf("Connected to\t: %s\n",IP());	
#endif
		Read_Last   = Read_Total = 0;	// Zero the byte reception counters
		return  (iSStatus=SocketStatusOk);			//iSStatus= SocketStatusOk;
		};
	}//	Code execution never reaches here so no return is needed
/*	Function	:	SERVER()
**	Parameters	:	F1_int	Portnumber
**			 	F1_int	Max_Connect	
**	Returns		:	F1_int
**	Description	:	Opens a socket in server mode.
*/
SocketStatus	Socket::SERVER(	F1_int Portnumber	,
				F1_int Max_Connect	)
	{
	F1_int T_Hand		=0,NF_Hand		=0,sizeof_sa_in=0;
	sizeof_sa_in		=sizeof(sa_in);
	iSPortnumber		=Portnumber;   

	zprintf("\n*** Initializing Server    ***");
	zprintf("\r*** Opening Primary Socket ***");
	zprintf("\r*** Socket Open OK         ***");
	struct hostent *hp;
	memset(&sa_in, 0, sizeof(struct sockaddr_in));	// clear our address 

	obSHostname=szHOST_NAME;

	hp= gethostbyname(obSHostname.toString());			// get our address info
	if(hp==NULL)
		{
		zprintf("gethostbyname(%s) Fails",obSHostname.toString());
		return (iSStatus=SocketStatusHostNotFound);
		}	// we don't exist !? 
	sa_in.sin_family	= hp->h_addrtype			;     
	sa_in.sin_port		= htons((unsigned short)iSPortnumber)	;// this is our port number 
	sa_in.sin_addr.s_addr   = INADDR_ANY				;

	if ((iSHandle= socket(AF_INET, SOCK_STREAM, 0)) < 0) /* create socket */ return( iSStatus=SocketStatusSocketFailed);	

	zprintf("\r*** Binding Socket         ***");
	if(	bind(HAND()				,
		(struct sockaddr *)&sa_in		,
		sizeof(struct sockaddr_in)		) < 0) 
		{
		zprintf("\n*** Binding Socket [FAILED]***\n");
		CLOSE();return(iSStatus=SocketStatusBindFailed);                         // bind address to socket 
		}
	zprintf("\r*** BIND OK                ***");
	zprintf("\r*** LISTENING on [%5.5d]   ***",iSPortnumber);
	if(listen(HAND() , Max_Connect )<0)
		{	// max # of queued connects 
		zprintf("\n*** ERROR LISTENING        ***");
		perror("Socket::SERVER() Listen()");
		CLOSE();exit(1);
		};
	zprintf("\n*** Reception Loop Begins  ***");

	signal(SIGCHLD	, Dr_KavorkianSIGCHLD	);		// This addresses Zombie children 
	signal(SIGPIPE	, Dr_KavorkianSIGPIPE	);		// This addresses Broken Pipes
	signal(SIGSEGV	, Dr_KavorkianSIGSEGV	);		// This addresses Broken Pipes

	for(;;++Hit_Cnt) // loop for phone calls 
		{
		// *** Deleting the CHATRUN file in the 
		// *** server's home directory will end 
		// *** the run at the next call.
//		if(access("CHATRUN",0))
//			{
//			zprintf("\n*** CHATRUN flag file absent\n*** Normal Shutdown\n");
//			break;
//			}
		// *** Wait for a socket to ring
		// *** and connect to it when it does ring.
		if (	(T_Hand= accept(HAND()						,
					(struct sockaddr *)	&sa_in		,
					(socklen_t *)		&sizeof_sa_in)) < 1	) // get a connection 
			{
			if (	errno==EAGAIN	||errno==EWOULDBLOCK	||
				errno==EBADF	||errno==EINTR		||
				errno==EPROTO	||errno==ECONNRESET	)continue;
			perror("::SERVER() accept");exit(1);    // BARF-N-DIE
			};
		// ***
		// ***	We now have a connection
		// ***
		zprintf("\r*** RING %d ***"		,
			Hit_Cnt				);
		// *** Are we using a customized 
		// *** function for RHF ( Receive Hit Function )
		if(RHF)
			{
			NF_Hand		=iSHandle	;
			iSHandle	=T_Hand		;
			if((*RHF)())// If we want to continue running
				{
				CLOSE();
				iSHandle=NF_Hand;continue;
				}else{;
				CLOSE();iSHandle=NF_Hand;CLOSE();
				zprintf("\n*** SERVER KILL Request\t***");
				}
			}else{	// *** Since there was no custom RHF  ( Receive Hit Function )
				// *** we'll provide some default 
				// *** behaviour here.

			Write_Total=Read_Total=Read_Last=Write_Last=0;
			switch(fork())	        // try to handle connection
				{
			case -1:		// bad news.  BARF-N-DIE 
				perror("fork");CLOSE();close(T_Hand);exit(1);
			case  0:		// we're the child, do something 
				CLOSE();	// Dispose of our clone of mothers hand;
				iSHandle=T_Hand;// Adopt new hand in place of mother's hand
				zprintf("\n*** Processing Hit [%6.6d] from [%15.15s] ***",
					Hit_Cnt						 ,
					IP()						);//CADDRESS(&sa_in)				);
				return (iSStatus=SocketStatusOk);
			default:
				close(T_Hand);	// dispose of our clone of childs hand 
				continue;	// Head up for another connection
				};
		  };//****End if RHF  ( Receive Hit Function )

	  break;
	  }; // End for(;FOREVER;) loop for phone calls 

	zprintf("*** End RUN\t***\n*** Dr. Clue Deamon class ***\n");
	//  ***** Nothing should ever reach here ******
	return (iSStatus=SocketStatusServerFailed);
	}
/*	Function	:	IS_READABLE()
**	Parameters	:	F1_int	W_Sec
**			 	F1_int	W_uSec
**	Returns		:	F1_int
**	Description	:	Tries to determine if this socket is readable
*/
F1_int	Socket::IS_READABLE(	F1_int W_Sec	,
				F1_int W_uSec	)
	{
	fd_set	readmap		;
	F1_int	i	=0	,
		cnt	=0	;

RSRetry:
	try	{
		if(cnt++ >10)throw(10000)	;if(!IS_OPEN())throw(EBADF);
		FD_ZERO(&readmap)		;FD_SET (iSHandle  ,&readmap  );
		TV_TIME.tv_sec=W_Sec		;TV_TIME.tv_usec=W_uSec;//SELECT_TIMEOUT;
		if((i=select(iSHandle+1,&readmap,NULL,NULL,&TV_TIME))<0)throw(errno);
		}
	catch(int &er)
		{
		switch(er)	{
		case EINTR	:	goto RSRetry;
		case 10000	:
		default		:	zprintf("\n *** SELECT ATTEMPT [%2.2d]",cnt);
						perror("Socket::IS_READABLE() select error=");
				}//END SWITCH
	}//END CATCH
	return (i>0);
	}    
/*	Function	:	IS_WRITABLE()
**	Parameters	:	F1_int	W_Sec
**			 	F1_int	W_uSec
**	Returns		:	F1_int
**	Description	:	Tries to determine of the current socket is writable.
*/
F1_int	Socket::IS_WRITABLE(	F1_int	W_Sec	,
				F1_int	W_uSec	)
	{
	fd_set	writemap	;
	F1_int	i=0,cnt=0	;
WSRetry:  
	try	{
		if(cnt++>10 )throw(10000)	;if(!IS_OPEN())throw(EBADF);
		FD_ZERO(&writemap)		;FD_SET (iSHandle,&writemap);
		TV_TIME.tv_sec=W_Sec		;TV_TIME.tv_usec=W_uSec;//SELECT_TIMEOUT;
		if((i=select(iSHandle+1,NULL,&writemap,NULL,&TV_TIME))<0)throw(1000);
		}
	catch (int &er)
		{
		switch(er)
			{
		case EINTR	:goto WSRetry;
		case 10000	:
		default		:perror("Socket::IS_WRITABLE() select error=");
			}// END CASE
		}// END CATCH
	return (i>0);
	}
/*	Function	:	NOBLOCK()
**	Parameters	:	F1_int	DHand
**	Returns		:	F1_int
**	Description	:	
*/
F1_int	NOBLOCK(F1_int SHand)
	{
	F1_int	flags	,
		rc	;
	flags	  = fcntl(SHand, F_GETFL, 0L	);
//	flags	 |= O_NDELAY			; /* maybe O_NDELAY or O_NONBLOCK; see the man pages */
	flags	 |= (O_NDELAY|O_NONBLOCK) 	; /* maybe O_NDELAY or O_NONBLOCK; see the man pages */
	if ((rc=fcntl(SHand, F_SETFL, flags))==-1)perror("fcntl F_SETFL");
	return flags;
	}
