New documentation for SDL

In preparation for SDL 0.7, I’ve updated the on-line documentation.

Please let me know how useful and correct it is:

http://www.devolution.com/~slouken/SDL/docs-0.7/

See ya!
-Sam Lantinga (slouken at devolution.com)–
Author of Simple DirectMedia Layer -
http://www.devolution.com/~slouken/SDL/

What of network support for SDL? I didn’t see anything about that in the
online docs. Online multiplayer games, each player on Win95, linux or BeOs,
all compatible—the idea gives me a woody :^).

Later–
Dave

What of network support for SDL? I didn’t see anything about that in the
online docs. Online multiplayer games, each player on Win95, linux or BeOs,
all compatible—the idea gives me a woody :^).

Each of those operating systems support the Berkeley sockets interface.
You can write your own network support, and it will be compatible with
each OS. :slight_smile:

Just for grins, the Win95 and UNIX versions of networked Maelstrom
are compatible (though not available for general release.)

See ya!
-Sam Lantinga (slouken at devolution.com)–
Author of Simple DirectMedia Layer -
http://www.devolution.com/~slouken/SDL/

There isn’t any doco for the screenlib stuff - could you knock some up
sometime?

TIA,
njh

There isn’t any doco for the screenlib stuff - could you knock some up
sometime?

Sure. The interface is fairly straight-forward, I’ll put some up along
with my Maelstrom port (which is going fairly slowly)

See ya!
-Sam Lantinga (slouken at devolution.com)–
Author of Simple DirectMedia Layer -
http://www.devolution.com/~slouken/SDL/

There isn’t any doco for the screenlib stuff - could you knock some up
sometime?

Sure. The interface is fairly straight-forward, I’ll put some up along
with my Maelstrom port (which is going fairly slowly)

Great! - then I can make my bro work it out, instead of me… :slight_smile:

njhOn Tue, 23 Jun 1998, Sam Lantinga wrote:

Good night. :slight_smile:
you might be interested in join the world of goofey. like IRC, but GPL…

njh
-------------- next part --------------
/* $Id: goofey.c,v 1.3 1997/03/18 02:22:21 tym Exp $ */
/*On Tue, 23 Jun 1998, Sam Lantinga wrote:
*

  • $RCSfile: goofey.c,v $

@(#)goofey.c 2.08 Pluto Client code.

  • This code maintainns connections with the central pluto server
  • and is used to inform it of the users activity.
  • Written: Darren Platt , Tim MacKenzie.
  • Started: Sometime
  • This was SCCS version: 2.02
  • History:
  • Daz v0.00: Goofey gasps its first breath of CPU time.
  • tym v1.11: Added check of tty owner to exit conditions… problem on decs
  • tym v1.12: Now checks time since last keypress as condition of death.
  • tym v1.15: Now uses statics for the ttyname rather than getting it everytime
  • tym v1.17: Uses grandparent pid for death (got it right finally).
  • daz v1.17: Better command line parsing.
  • daz v1.18: Once more for good luck so that -s works correctly.
  • tym v1.19: Changed tty code, dont call ttyslot when we’re not on a tty.
  • tym 31 Oct 1991: Modified addr code. Clock can tell goofey to stay around
  •  after a query. Now prompts for message of none given to -s.
    
  • daz v1.22: Changed so that exit codes are indicative of exit status.
  • tym 6 Apr 1992: Checks for tty, removed ‘Arrived_message’,
  •  No more static limits! Removed a lot of dead code.
    
  • tym 7 Apr 1992: Added ‘.’ on line as exit condition for ‘-s’
  • tym 8 Apr 1992: Fixed the problem with pluto restarts (can accept() while
  •  waiting for pluto to come back to life).
    
  • tym 13 Apr 1992: Fixed problem with sgi’s stuff getpw*
  • tym 14 Apr 1992: Another try at proper user identification.
  • tym 15 Apr 1992: Problem with idle time?
  • tym 15 Apr 1992: Now ignores SIGTTOU… stty tostop was killing it!
  • tym 10 Jun 1992: Restores tty modification times after writing if able.
  • tym 20 Jun 1992: Sends password if it is found.
  • tym 20 Jun 1992: Only allow connections from the server machine.
  • tym 12 Oct 1992: Added anthony’s FIREWALL changes, protocol change.
  • tym 3 Dec 1992: Added GOOFEYUSER environment variable for rik.
  • tym 7 Dec 1992: Now bzero’s sockaddr’s before using them.
  • tym 8 Dec 1992: Ansi’ized ?
  • tym 7 Apr 1993: Checks result of read since linux is slightly broken.
  • tym 27 May 1993: Changed way that we declare funcs so it’s neater :slight_smile:
  • tym 7 Dec 1993: configure’ized. Added client options, width handling
  • tym 16 Dec 1993: added readline/getline support (M Battersby/A Cosgriff)
  • tym 23 Dec 1993: made non-blocking broken by default, fixed getenv proto
  • tym 26 Aug 1994: added GOOFEYLOC support
  • tym 29 Aug 1994: fixed problem with readline and eof.
  • tym 21 Jul 1995: fixed problem with long lines and readline/getline
  • tym 22 Sep 1995: Added editor patches from Karl, extended them somewhat
  • tym 23 Sep 1995: Allow ~r to read output from command
  • tym 2 Oct 1995: Fixed to keep non-ansi compilers happy.
  • tym 6 Oct 1995: Fixed ~c… stupid ?: precedence…
  • tym 3 Apr 1996: Added autosplit
  • tym 26 Sep 1996: Added “I don’t know” idletime.
  • tym 29 Oct 1996: Added variable timeout and re-register code
  • tym 24 Feb 1997: Fixed small problem with GOOFEY_ARGS
  • tym 18 Mar 1997: Changed hostname and added session_pwd stuff
    */

#define VERSION “3.15”
#define CLIENT_TYPE ‘G’
#define PWD_FILE “.goofeypw”

static char *id_string = “$Id: goofey.c,v 1.3 1997/03/18 02:22:21 tym Exp $”;

#ifndef MAX_WAIT
#define MAX_WAIT 20
#endif
int max_wait = MAX_WAIT;

#define NEWSERVER “130.194.9.2”
#ifndef SERVER_HOSTNAME
#define SERVER_HOSTNAME “pluto.cc.monash.edu.au
#endif

#ifndef SERVER_PORT_NO
#define SERVER_PORT_NO 3987
#endif

#define LONELY_INTERVAL 60
#define MAX_RETRIES 10
#define SERVER_MAXREQ 999 /* Maximum size message server will handle */
#define I_DONT_KNOW 200000000

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <ctype.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <errno.h>
extern int errno;

#ifdef READLINE
#include <readline/readline.h>
#else
#ifdef GETLINE
#include <getline.h>
#endif
#endif

#define TRUE 1
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#include <stddef.h>
#endif

/* Wierd macros for handling ansi/non-ansi prototypes */
#if ANSI || GNUC || STDC
#define AND ,
#define DEFUN(a,b) ( b )
#define VOIDFUNC ( void )
#define _PROTO(a) a
#else
#define AND ;
#define DEFUN(a,b) a b;
#define _PROTO(a) ()
#define VOIDFUNC ( )
#endif

/* For firewall use:

  • Define FIREWALL to be the minumum local port to use for outgoing connections
  • and FIREWALL_MAX to be the maximum port. LOCAL_PORT - LOCAL_PORT_MAX are
  • the port range over which we will accept connections for resident goofeys
    */
    #ifdef FIREWALL

ifndef FIREWALL_MAX

define FIREWALL_MAX FIREWALL

endif

ifndef LOCAL_PORT

define LOCAL_PORT FIREWALL

endif

ifndef LOCAL_PORT_MAX

define LOCAL_PORT_MAX FIREWALL_MAX

endif

#endif

#ifndef LOCAL_PORT

define LOCAL_PORT 0

#endif

#ifndef LOCAL_PORT_MAX

define LOCAL_PORT_MAX LOCAL_PORT

#endif

/* Portability stuff: if we don’t have fd_set or FD_SET,etc macros

  • then make them up.
    */
    #ifndef FD_SET

ifndef SUNOS3

typedef struct fd_set { int fds_bits[1]; } fd_set;

endif

define FD_ZERO(fd) ((fd)->fds_bits[0] = 0)

define FD_SET(b, fd) ((fd)->fds_bits[0] |= 1 << (b))

define FD_ISSET(b, fd) ((fd)->fds_bits[0] & 1 << (b))

define FD_SETSIZE 32

#endif

/* Handle bsd/sysv differences with string handling /
#if STDC_HEADERS || HAVE_STRING_H
#include <string.h>
/
An ANSI string.h and pre-ANSI memory.h might conflict. /
#if !STDC_HEADERS && HAVE_MEMORY_H
#include <memory.h>
#endif /
not STDC_HEADERS and HAVE_MEMORY_H /
#define index strchr
#define rindex strrchr
#define bcopy(s, d, n) memcpy ((d), (s), (n))
#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
#define bzero(s, n) memset ((s), 0, (n))
#else /
not STDC_HEADERS and not HAVE_STRING_H /
#include <strings.h>
/
memory.h and strings.h conflict on some systems. /
#endif /
not STDC_HEADERS and not HAVE_STRING_H */

#ifndef RETSIGTYPE
#define RETSIGTYPE void
#endif

static struct sockaddr_in pluto_host;
char * build_arrived_string _PROTO((int));
void check_alloc _PROTO((void *, int));
int connect_to_server _PROTO((void));
void do_resize _PROTO((char **,int *,int,char *));
void establish_signal_handlers _PROTO((void));
char * get_my_tty _PROTO(());
void go_find_a_port _PROTO((void));
void print_help _PROTO((void));
void process_flags _PROTO((char **));
void request_handler _PROTO((void));
void say_hi_to_server _PROTO((int));
long when_last_keypress _PROTO((void));
void write_str _PROTO((int, char *));
void check_size _PROTO((void));
void process_env _PROTO((void));
void generate_password _PROTO((void));
char * get_message _PROTO((char **));
int write_goof _PROTO((char *s , char *name));
extern char *getenv();

int local_sock;
int ppid; /* Parent process id /
int port_no; /
Port number we have reserved for ourselves at this end /
int want_help;
int reconnect_retries; /
Number of tries left to reconnect to the server /
int width; /
Width I think my terminal is /
int multipart; /
A kluge to handle multipart messages /
int lonely_timer; /
How long is it since we’ve spoken/heard from server? */

#define REREGISTER_TIME 600 /* How long to wait before re-registering */

#define RESIZE(m,l,x,s) do_resize(&m,&l,x,s)

/* Where the server resides. Can be changed at command line. */
char machine_name[50] = SERVER_HOSTNAME;
int port_number = SERVER_PORT_NO;
char optstring[100] = “P”;
int broke_shutdown;
char session_pwd[20];

int
main DEFUN((argc,argv), int argc AND char ** argv)
{
check_size();
process_env();
process_flags(argv); /* This may exit() if goofey is non-resident /
if (when_last_keypress() < 0) {
fprintf(stderr,
"\rgoofey: Parent process died, exiting\n");
exit(0);
}
go_find_a_port(); /
Assumed to work or die. */
generate_password();
say_hi_to_server(1);

ppid = getppid();

if (fork()) {
exit(0); /* In parent */
}
else {
fd_set mask;
struct timeval tv;
establish_signal_handlers(); 
while(1) {
    request_handler();
    while (reconnect_retries) {
	int i;
	FD_ZERO(&mask);
	FD_SET(local_sock, &mask);
	tv.tv_sec = LONELY_INTERVAL;
	tv.tv_usec = 0;
	i=select(local_sock+1,&mask,0,0,&tv);
	if (i==1)
	    request_handler();
	if (i<0 && errno != EINTR) {
	    fprintf(stderr,"\rSomething has happened to my socket!\n");
	    exit(1);
	}
	say_hi_to_server(0);
    }
}
}

}

void
check_size VOIDFUNC
{
#ifdef TIOCGWINSZ
struct winsize ws;
#endif

if (isatty(1)) {
char *s;

#ifdef TIOCGWINSZ
if (ioctl(1,TIOCGWINSZ,&ws))
return;
width = ws.ws_col;
#endif
s = getenv(“COLUMNS”);
if (s)
width = atoi(s);
}
}

int
autoSplit VOIDFUNC
{
return strchr(optstring, ‘A’) != 0;
}

void
say_hi_to_server DEFUN((disp), int disp)
{
int sock;
char *mesg;
char buf[100];
int i;

sock = connect_to_server();	 /* Works or dies. */
if (sock < 0) return;
mesg = build_arrived_string(1);
if (broke_shutdown) {
sprintf(buf,"!%03x",strlen(mesg));
write_str(sock,buf);
write_str(sock,mesg);    /* Send the initial message */
write_str(sock,"!000");  /* Send "eof" */
} else {
write_str(sock,mesg);    /* Send the initial message */
shutdown(sock,1); /* Send Eof */
}
while ((i = read(sock,buf,(sizeof buf)-1)) > 0) {
buf[i] = 0;
if (disp)
    printf("%s",buf);
if (broke_shutdown && !buf[i-1])
    break;
}
fflush(stdout);
close(sock);

}

void
generate_password VOIDFUNC
{
char *tmp = build_arrived_string(0);
int i;
unsigned long seed = time(0L);
seed ^= (getpid() << 16) | (getppid());
for (i=0;tmp[i];i++)
seed ^= (tmp[i] << ((i%4) * 8));
sprintf(session_pwd, “%08lx”, seed);
}

void
die VOIDFUNC
{
int sock;
char buff[1024];
sock = connect_to_server(); /* Works or dies. /
sprintf(buff,"%s,%s",build_arrived_string(0),"-2");
write_str(sock,buff);
close(sock);/
Say bye to server */
exit(0);
}

RETSIGTYPE
sigdie DEFUN((sig), int sig)
{
fprintf(stderr,"\rgoofey: received signal %d, exiting\n",sig);
die();
}

void
send_and_get_message DEFUN((str1,str2), char * str1 AND char * str2)
{
int sock;
fd_set tm;
struct timeval tout;
int dont_exit = 0;
char buff[1025];
char *b;
int i;

sock = connect_to_server();	 /* Works or dies. */
if (sock < 0) return;
b = malloc(i=(strlen(build_arrived_string(0)) +
	  strlen(str1) +strlen(str2) + 4));
check_alloc(b,i);
if (i > SERVER_MAXREQ) {
fprintf(stderr,"goofey: server may truncate message. \

Saving to ‘dead.goofey’ just in case\n");
if (write_goof(str2,“dead.goofey”)) {
fprintf(stderr,“goofey: unable to write to file ‘dead.goofey’\n”);
}
}
sprintf(b,"%s,%s %s\n",build_arrived_string(0),str1,str2);
if (broke_shutdown) {
sprintf(buff,"!%03x",strlen(b));
write_str(sock,buff);
write_str(sock,b);
write_str(sock,"!000");
} else {
write_str(sock,b);
shutdown(sock,1); /* Send eof */
}
free(b);
i=1;
FD_ZERO(&tm);
FD_SET(sock,&tm);
while (i>0) {
tout.tv_usec= 0;
tout.tv_sec = max_wait;

if (select(sock +1,&tm,0,0,&tout)<=0) {
    printf("Pluto failed to reply within %d seconds\n",max_wait);
    break;
}
i = read(sock,buff,(sizeof buff)-1);
if (i<0) break;
buff[i]=0;
if (i && buff[0] == 1) dont_exit = 1;
printf("%s",buff);
fflush(stdout);
if (broke_shutdown && !buff[i-1])
    break;
}
close(sock);/* Say bye to server */
if (dont_exit) return;
if (multipart) return;
exit(0);  /* The normal exit point for non-resident goofeys */

}

RETSIGTYPE
lonely DEFUN((ignored), int ignored)
{ /* Am I lonely and forgotten ? */
int i;
i = when_last_keypress();

if (i < 0) {
fprintf(stderr,"\rgoofey: parent process died, exiting\n");
die();
}

signal(SIGALRM,lonely);
alarm(LONELY_INTERVAL);
lonely_timer += LONELY_INTERVAL;
if (lonely_timer > REREGISTER_TIME) {
int tmp = reconnect_retries;
reconnect_retries = 2; /* just in case this fails */
say_hi_to_server(0);
reconnect_retries = tmp;
}

}

int
allspace DEFUN((s), char *s)
{
while (s && *s && isspace(*s)) s++;
if (!s || !(*s)) return 1;
return 0;
}

char *
get_users_name VOIDFUNC
{/* Find out the users name, do not modify this code unless absolutely

  • necessary (for your own protection)
    */
    static char *name;
    if (name) return name;
    if (!name || allspace(name))
    name = getenv(“GOOFEYUSER”);
    if (!name || allspace(name))
    name = getenv(“USER”);
    if (!name || allspace(name))
    name = getenv(“LOGNAME”);
    if (!name || allspace(name))
    name = “unknown”;
    return name;
    }

char *
build_arrived_string DEFUN((force), int force)
{
/* Construct an arrived message giving details of the assigned port
* number and the username etc. */

static char data[300];
char buf[100];
char *loc,*s;
struct stat st;
static done,port,wid;

if (done && port==port_no && wid == width && !force) return data;
done =1;
port = port_no;
wid = width;
sprintf(buf,"%s/%s",getenv("HOME"),PWD_FILE);
if (stat(buf,&st))
buf[0]=0;
else {
FILE *f;
if (st.st_mode & 077) {
    fprintf(stderr,"goofey: warning %s should have permission 0600!\n",
	buf);
    fprintf(stderr,"goofey: type 'chmod 600 %s' to fix this\n",buf);
}
f = fopen(buf,"r");
buf[0] = 0;
if (f) {
    fread(buf,1,10,f);
    fclose(f);
    buf[10] = 0;
}
}

if (strchr(optstring,'S'))
broke_shutdown = 1;

loc = getenv("GOOFEYLOC");
if (loc)
for (s = strchr(loc, '&') ; s ; s = strchr(s+1, '&'))
    *s = '*';

sprintf(data,"#%c%s%s%s,|%d,%s%s%s#%s,%s,%s,%d,%s",CLIENT_TYPE,VERSION,
*optstring?",@":"",optstring,
width, 
loc?"&":"", loc?loc:"", loc?"&,":"",
session_pwd,
get_users_name(),buf,port_no,get_my_tty() );
return data;

}

void
go_find_a_port VOIDFUNC
{
/* Allocate a port that the server can query this client through */

int length;
int count;
struct sockaddr_in local_server;

local_sock = socket(AF_INET,SOCK_STREAM,0);
if (local_sock <0) {
perror ("Opening stream socket :- couldn't get a socket");
exit(1);
}

bzero(&local_server,sizeof local_server);
local_server.sin_family = AF_INET;
local_server.sin_addr.s_addr = INADDR_ANY;

for (count = LOCAL_PORT; count <= LOCAL_PORT_MAX;count++) {
local_server.sin_port = htons(count);
if (bind(local_sock,(struct sockaddr *) &local_server,
	 sizeof local_server) == 0)
    break;
}
if (count > LOCAL_PORT_MAX) {
fprintf(stderr,
    "goofey: couldn't allocate local port\n");
exit(1);
}

length = sizeof local_server;
if (getsockname(local_sock,(struct sockaddr *)&local_server,&length) <0) {
perror("getsockname");
exit(1);
} /* Have established a port which the clock local_server may use to
* interrogate this process.
   */

port_no = ntohs(local_server.sin_port);

#ifdef DIAGNOSTICS
fprintf(stderr,"%s\n",inet_ntoa(local_server.sin_addr));
printf(“Socket port #%d\n”,ntohs(local_server.sin_port));
#endif
}

long
when_last_keypress VOIDFUNC
{
struct stat my_stat;
if (kill(ppid,0)) return -3; /* My daddy is dead :frowning: */
if (stat(get_my_tty(),&my_stat)) return I_DONT_KNOW;
if (time(0) < my_stat.st_mtime) return 0;
return time(0) - my_stat.st_mtime;
}

char *
get_my_tty VOIDFUNC
{
char *ttyname();
char *s;
static set;
static char buffer[30];

if (set) return buffer;
s = ttyname(0);
if (!s) strcpy(buffer,"/dev/notatty");
else strcpy(buffer,s);
set =1;
return buffer;

}

void
request_handler VOIDFUNC
{
int msgsock;
char buf[1024];
struct sockaddr_in addr;
int len = sizeof addr;
char *msg=0;
int rval,size=0;
long int last=0;

msgsock = accept(local_sock,(struct sockaddr *)&addr,(int *)&len);
if (msgsock == -1) 
return;

lonely_timer = 0;

do {
if ((rval = read(msgsock,buf,1023)) < 0) {
    if (errno != EINTR) {
	perror("Handling clock request.");
	break;
    }
} else {
    if (size == 0) {
	if (strchr("ZW",*buf)) {
	    char tmp[100];
	    sprintf(tmp,"%ld\n",last=when_last_keypress());
	    write_str(msgsock,tmp);
	}
    }
    RESIZE(msg,size,rval,buf);
}
} while (rval != 0);
if (last < 0) {
fprintf(stderr,"\rGoofey: parent process has died, exiting\n");
exit(0);
}
if (msg) {
if (*msg && strncmp(msg+1, session_pwd, strlen(session_pwd))) {
    fprintf(stderr,"\n\rGoofey: Message received from %s with invalid session password (intruder?)\n",inet_ntoa(addr.sin_addr));
    free(msg);
    close(msgsock);
    return;
}
msg[size] = 0;
switch(msg[0]) {
    case 'S':
	/* Attempt to reconnect to clock at 1 minute intervals */
	reconnect_retries=MAX_RETRIES;
	break;
    case 'W':
	/* Return time since last keypress */
	break;

    case 'Z':
	/* Why not. */
    {
	struct stat times;
	time_t timeout[2];
	int doit;
	doit = stat(get_my_tty(),&times); 
	    /* Store the access and mod times */
	puts(msg+1+strlen(session_pwd)); /* Output a message */
	/* Reset the access and modify time to value before blatting */
	if (!doit) {
	    timeout[0] = times.st_atime;
	    timeout[1] = times.st_mtime;
	    utime(get_my_tty(),timeout);
	}
	break;
    }

    case 'E':
	/* Exit */
	close(local_sock);
	close(msgsock);
	sleep(1);
	fprintf(stderr,
    "\rgoofey: resident goofey killed by server at your request\n");
	exit(0);

    default:
	write_str(msgsock,"Unknown command to goofey\n");
}
free(msg);
}
close(msgsock);

}

void
find_inet_addr DEFUN((ad), struct sockaddr_in * ad)
{/* Find address by lookup once */
struct hostent *hp, gethostbyname();
static int done=0; /
Have I found it yet? */
ad->sin_family = AF_INET;
if (done) {
ad->sin_addr.s_addr = pluto_host.sin_addr.s_addr;
return;
}

pluto_host.sin_addr.s_addr = inet_addr(machine_name); 
/* Is it a 130.x.x.x ? */
if (pluto_host.sin_addr.s_addr != -1) { /* YES! */
pluto_host.sin_family = AF_INET;
} else {/* Must be a hostname */
hp = gethostbyname(machine_name);
if (hp) {
    pluto_host.sin_family = hp->h_addrtype;

#if defined(h_addr) /* In 4.3, this is a #define /
bcopy(hp->h_addr_list[0],(caddr_t)&pluto_host.sin_addr,
hp->h_length);
#else /
defined(h_addr) /
bcopy(hp->h_addr,(caddr_t)&pluto_host.sin_addr, hp->h_length);
#endif /
defined(h_addr) */
} else {
fprintf(stderr,"%s: unknown host\n", machine_name);
exit(2);
}
}
ad->sin_addr.s_addr = pluto_host.sin_addr.s_addr;
done =1;
}

int
connect_to_server VOIDFUNC
{
/* Establish connection with the central server */

int sock;
int res;
struct sockaddr_in server;

sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock<0) {
perror("opening stream socket");
exit(1);
}
bzero(&server,sizeof server);
server.sin_family = AF_INET;

#ifdef FIREWALL
{
int count;
server.sin_addr.s_addr = INADDR_ANY;
for (count = FIREWALL; count <= FIREWALL_MAX;count++) {
server.sin_port = htons(count);
if (bind(sock,(struct sockaddr *) &server,
sizeof server) == 0)
break;
}
if (count > FIREWALL_MAX) {
fprintf(stderr,
“goofey: couldn’t allocate local port for firewall\n”);
exit(1);
}
}
#endif

find_inet_addr(&server);
server.sin_port = htons(port_number);

#define NONBLOCK_BROKE
#ifndef NONBLOCK_BROKE

ifdef FNDELAY

if (fcntl(sock,F_SETFL,FNDELAY))

else

if (fcntl(sock,F_SETFL,O_NONBLOCK))

endif

{
perror("Setting non blocking mode on connecting socket");
exit(1);
}

#endif

res = connect(sock,(struct sockaddr *)&server,sizeof server);

#ifndef NONBLOCK_BROKE
/* These #if’s are a bit ugly … if nonblocking is broken for some
* reason then don’t fcntl and don’t select.
*/
signal(SIGPIPE,SIG_IGN);
if ( res < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
fd_set tm;
struct timeval tout;
tout.tv_usec= 0;
tout.tv_sec = max_wait;
FD_ZERO(&tm);
FD_SET(sock,&tm);
res = select(sock+1,0,&tm,0,&tout);
if (res > 0) {
res = connect(sock,(struct sockaddr )&server,sizeof server);
if (res < 0 && errno == EISCONN)
res = 1;
}
} else
#else
/
If non blocking is broken then set res to 1 if the connect succeeded */
if ( ! res)
#endif
{
res = 1;
}

if (res <= 0) {
if (reconnect_retries > 1) {
    close(sock);
    sock = -1;
    reconnect_retries--;
} else if (reconnect_retries == 1) {
    fprintf(stderr,"\rGoofey: lost connection to server\n");
    exit(1);
} else {
    if (res < 0)
	fprintf(stderr,
	    "goofey: The server is currently unavailable.\n");

#ifndef NONBLOCK_BROKE
else
fprintf(stderr,
“goofey: Timed out while connecting to server.\n”);
#endif
if (want_help) print_help();
exit(1);
}
} else {
reconnect_retries=0;
lonely_timer = 0;
}
#ifndef NONBLOCK_BROKE
signal(SIGPIPE,SIG_DFL);
fcntl(sock,F_SETFL,0);
#endif
return sock;
}

void
write_str DEFUN((sock,mesg),int sock AND char *mesg)
{
if (write(sock,mesg,strlen(mesg)) <0)
perror(“writing on stream socket”);
}

#ifdef SIGWINCH
RETSIGTYPE
size_change DEFUN((ignored),int ignored)
{
int owidth;
owidth = width;
check_size();
say_hi_to_server(0);
}
#endif

void
establish_signal_handlers VOIDFUNC
{
signal(SIGINT,sigdie);
signal(SIGQUIT,sigdie);
signal(SIGHUP,sigdie);
signal(SIGALRM,lonely);
signal(SIGTTOU,SIG_IGN);
#ifdef SIGWINCH
signal(SIGWINCH,size_change);
#endif
alarm(LONELY_INTERVAL);

listen(local_sock,5);

}

/* -------------------------------------------------------------------- *

  • Command line processing *
  • -------------------------------------------------------------------- */

int
check_split_arg DEFUN((argv,i),char ** argv AND int * i)
{
/* Some people get stressed when asked to provide options immediately
* after the command line flag, so we will give them the option of
* supplying the option with a space !!
* This routine advances the i variable if necessary etc.
*/

 ++argv[*i]; /* Advance past the flag itself */
 if (!strlen(argv[*i])) {
/* No more string left for this arg, so try the next one. */
(*i)++;
if (!argv[*i]) {
    /* No more args left, how tragic. */
    return 0;
}
return 1;
}
/* Argument follows the flag */
return 1;

}

struct { char *name; char *opt; } longopts[] = {
{“no-blat”, “B”},
{“only-one”, “1”},
{“return-response”, “R”},
{“inform-replace”, “I”},
{“forget-fail”, “F”},
{“tag-success”, “T”},
{“format-outgoing”,“f”},
{“shutdown-broke”,“S”},
{“ignore-alias”,“E”},
{“auto-split”,“A”},
{0,0}
};

void
process_env VOIDFUNC
{
char *tmpargv[40];
char *env = getenv(“GOOFEY_ARGS”);
int i = 1;
if (!env)
return;
tmpargv[0] = “goofey”;
while (*env) {
while (*env && isspace(*env)) env++;
if (!*env) break;
if (*env != ‘+’ && !(*env == ‘-’ && env[1] == ‘-’)) {
fprintf(stderr,
“goofey: GOOFEY_ARGS may only contain ‘+’ or ‘–’ args. Ignored\n”);
return;
}
tmpargv[i++] = env;
while (*env && !isspace(*env)) env++;
if (*env) {
*env = 0;
env++;
}
if (i == 40 - 1) {
fprintf(stderr,“goofey: GOOFEY_ARGS too long, truncated\n”);
break;
}
}
if (i <= 1)
return;
tmpargv[i] = 0;
process_flags(tmpargv);
}

void
process_flags DEFUN((argv),char *argv)
{
/
Process flags sequentially */
int i;
int ignore_rest = 0;
int send_message = 0;
char *m=0;
int len=0;
int got_args=0;

  for(i=1;argv[i]!=NULL;i++) {
    if (i && argv[i-1] == 0) {
	fprintf(stderr,"goofey: missing argument to option\n");
	exit(1);
    }
    if (ignore_rest) {
	 /* we have found a piece of plain text */
	RESIZE(m,len,strlen(argv[i]),argv[i]);
	RESIZE(m,len,1," ");
	got_args = 1;
	continue;
    } else if (*argv[i] == '+') {
	strcat(optstring,argv[i]+1);
	continue;
    } else if (*argv[i] != '-') {
	fprintf(stderr,"Expected an argument beginning with '-'\n");
	exit(1);
	break;
    } else if (argv[i][1] == '-') {
	/* Handle --options */
	char *s; 
	int j;
	argv[i]+=2;
	s = argv[i];
	if (!strncmp(s,"port",4) || (*s == 'p' && !isalpha(s[1]))) {
	    if ((s = strchr(s,'='))) {
		port_number = atoi(s+1);
	    } else {
		port_number = atoi(argv[++i]);
	    }
	    continue;
	} else if (!strncmp(s,"machine",7) ||
		(*s == 'm' && !isalpha(s[1]))) {
	    if ((s = strchr(s,'='))) {
		strcpy(machine_name,s+1);
	    } else {
		if (argv[++i])
		    strcpy(machine_name,argv[i]);
	    }
	    continue;
	} else if (!strncmp(s,"width",5)) {
	    if ((s = strchr(s,'='))) {
		width = atoi(s+1);
	    } else {
		width = atoi(argv[++i]);
	    }
	    continue;
	} else if (!strncmp(s,"timeout",7)) {
	    if ((s = strchr(s,'='))) {
		max_wait = atoi(s+1);
	    } else {
		max_wait = atoi(argv[++i]);
	    }
	    continue;
	} else {
	    for (j = 0;longopts[j].name;j++)
		if (!strcmp(s,longopts[j].name)) {
		    strcat(optstring,longopts[j].opt);
		    break;
		}
	    if (longopts[j].name)
		continue;
	}
    }
    while (*(++argv[i])!= 0) {
     switch(*(argv[i])) {
	case 'v':
	    printf("Goofey version %s\n",VERSION);
	    break;
	case 's':
	    /* Send message to user */
	    if (!check_split_arg(argv,&i)) {
		fputs("Expected user's name.\n",stderr);
		print_help();
		exit(1);
	    }
	    RESIZE(m,len,2,"*s");
	    RESIZE(m,len,strlen(argv[i]),argv[i]);
	    RESIZE(m,len,1," ");
	    send_message = ignore_rest = 1;
	    *(argv[i]+1)= 0;
	    break;
	case 'h':
	    want_help = 1;
	default: {
	    /* Goofey doesn't understand the command, so send it to the
	     * clock. */
	    
	    RESIZE(m,len,1,"*");
	    RESIZE(m,len,strlen(argv[i]),argv[i]);
	    RESIZE(m,len,1," ");
	    *(argv[i]+1)= 0;
	    ignore_rest = 1;
	}
    }
}
}
if (m) {
char *n;
if (send_message && !got_args) {
    n = get_message(&m);
    if (!n) {
	fprintf(stderr,"goofey: Empty message: not sent!\n");
	exit(0);
    }
    if (autoSplit()) {
	int otherlen = strlen(m) + strlen(build_arrived_string(0)) + 5;
	if (strlen(n) + otherlen > SERVER_MAXREQ) {
	    char *tmp, *end, *orig, old;
	    int part = 0;
	    fprintf(stderr, 
		"goofey: message exceeds maximum, autosplitting...\n");
	    tmp = n;
	    multipart = 1;
	    while (*tmp) {
		end = tmp + strlen(tmp);
		if (end - tmp > SERVER_MAXREQ - otherlen) {
		    end = tmp + SERVER_MAXREQ - otherlen;
		}
		orig = end;
		while (end > tmp && *end && *end != '\n') end--;
		if (end == tmp) {
		    end = orig;
		} else if (*end) {
		    end++;
		}
		old = *end;
		*end = 0;
		part++;
		fprintf(stderr,"goofey: sending part %d\n", part);
		send_and_get_message(m,tmp);
		*end = old;
		tmp = end;
	    }
	    fprintf(stderr,"goofey: Message sent in %d parts\n",part);
	    exit(0);
	} else {
	    send_and_get_message(m,n?n:"");
	}
    } else {
	send_and_get_message(m,n?n:"");
    }
    if (n)
	free(n);
} else {
    send_and_get_message(m,"");
}
free(m);
}

}

char *
editor VOIDFUNC
{
int l;
static char *e;
char *ed;
if (e)
return e;
ed=getenv(“VISUAL”);
if (!ed)
{
ed=getenv(“EDITOR”);
if (!ed)
ed=“vi”;
}
l=strlen(ed);
e=malloc(++l);
check_alloc(e,l);
strcpy(e, ed);
return e;
}

int
write_goof DEFUN((s, name), char *s AND char *name)
{
int fd;
int len=0;
fd = creat(name, 0600);
if (fd < 0) {
return 1;
}
if (s) {
len = strlen(s);
if (len != write(fd, s, len)) {
unlink(name);
close(fd);
return 1;
}
}
close(fd);
return 0;
}

char *
editmsg DEFUN((msg,len), char * msg AND int * len)
{
FILE *fp = 0;
char tmpfile[32];
char buf[500];
int i;

for (i = 'a';i<='z';i++) {
sprintf(tmpfile, "/tmp/goof%05lu%c", (long)getpid(), i);
if (!write_goof(msg, tmpfile)) {
    i = 0;
    break;
}
}
if (i) {
fprintf(stderr, "goofey: unable to create file '%s'\n", tmpfile);
return msg;
}
sprintf(buf, "%s %s", editor(), tmpfile);
fprintf(stderr,"goofey: invoking editor (%s)...\n",buf);
system(buf);
fp=fopen(tmpfile,"r");
if (!fp) {
fprintf(stderr, "\rgoofey: temp file cannot be accessed\n");
return msg;
}

free(msg);	/* We can discard existing msg now */
msg=0;
*len=0;
while (fgets(buf, sizeof(buf)-1, fp)!=NULL) {
RESIZE(msg,(*len),strlen(buf),buf);
}
fclose(fp);

unlink(tmpfile);
return msg;

}

char *
readln DEFUN((prompt,extra), char *prompt AND char **extra)
{
char *ln;
static char buf[400];
*extra = 0;
#ifdef READLINE
if (prompt) {
ln = readline(prompt);
extra = “\n”;
} else
#else
#ifdef GETLINE
if (prompt) {
ln = getline(prompt);
} else
#else
if (prompt) {
printf(prompt);
fflush(stdout);
}
#endif
#endif
/
This is called if we have no prompt for GETLINE and READLINE,
* and always if neither of them are set
*/
{
if (!fgets(buf,(sizeof buf)-1,stdin))
return 0;
ln = buf;
}
return ln;
}

char *
strip_ws DEFUN((s), char *s)
{
char *t;
if (!s)
return 0;
while (*s && isspace(*s)) s++;
for (t = s; *t && !isspace(t); t++) {
/
Nothing */
}
if (*t)
*t = 0;
return s;
}

void
save_goof DEFUN((s), char *s)
{
char *ln;
char *junk;
ln = readln("Enter filename to save to> ", &junk);
ln = strip_ws(ln);
if (!ln || !*ln) {
fprintf(stderr,“goofey: Message not saved\n”);
return;
}
if (write_goof(s, ln)) {
fprintf(stderr,“goofey: Could not save to ‘%s’\n”,ln);
return;
}
fprintf(stderr,“goofey: Message saved to ‘%s’\n”,ln);
}

void
read_file DEFUN((str,len), char **str AND int *len)
{
int pipe = 0;
char *ln;
FILE *f;
char buf[100];
char *junk;

ln = readln("File to read from (or !command)> ",&junk);
if (ln && *ln == '!') {
f = popen(ln + 1, "r");
if (!f) {
    fprintf(stderr,"goofey: could not execute '%s'\n",ln+1);
    return;
}
pipe = 1;
} else {
ln = strip_ws(ln);
if (!ln || !*ln) {
    return;
}
f = fopen(ln, "r");
if (!f) {
    fprintf(stderr,"goofey: Could not open file '%s'\n",ln);
    return;
}
}
while (fgets(buf, sizeof(buf)-1, f)!=NULL) {
printf("%s",buf);
RESIZE(*str,(*len),strlen(buf),buf);
}
if (pipe) {
int status;
status = pclose(f);
if (status)
    fprintf(stderr,"goofey: command returned error code %d\n",status);
} else {
fclose(f);
}
return;

}

void
get_recip DEFUN((recip), char **recip)
{
char *ln,*junk;
int len = 0;
printf(“Currently to: %s\n”,(*recip)+2);
ln = readln(“Enter (comma separated) list of recipients> “,&junk);
ln = strip_ws(ln);
if (!ln || !*ln) {
fprintf(stderr,“goofey: recipients not changed\n”);
return;
}
free(*recip);
*recip = 0;
RESIZE(*recip,len,2,”*s”);
RESIZE(*recip,len,strlen(ln),ln);
RESIZE(*recip,len,1," ");
fprintf(stderr,“goofey: recipients changed to: %s\n”,ln);
}

char *
get_message DEFUN((recip), char *recip)
{
int prompt=isatty(0); /
Only prompt on tty’s */
char *s;
int len;
char *ln,*extra=0;

s = 0;
len = 0;

if (prompt)
printf("Enter your message: (blank line or ^D to end, ~h for help)\n");
while (1) {
int i;
fflush(stdout);

ln = readln(prompt?"> ":0, &extra);
if (ln && (extra || *ln)) {
    int meta=0;
    int textlen;
    textlen = i = strlen(ln);
    if (ln[i-1] == '\n')
	textlen = i-1;
    if (prompt && textlen == 2 && *ln=='~') {
	meta=1;
	switch(ln[1]) {
	case 'v':
	case 'e':
	    s = editmsg(s, &len);
	    break;
	case 't':
	    get_recip(recip);
	    break;
	case 'p':
	    printf("[ To: %s]\n",(*recip) + 2);
	    if (s) {
		printf("%s",s);
	    }
	    break;
	case '.':
	    textlen = 0;
	    break;
	case 'q':
	    if (write_goof(s,"dead.goofey")) {
		fprintf(stderr,
		    "goofey: Could not write to file 'dead.goofey'\n");
		break;
	    }
	    fprintf(stderr,
	    	"goofey: Message written to file 'dead.goofey'\n");
	    /* Fall through */
	case 'x':
	    fprintf(stderr,"goofey: Quitting: message not sent\n");
	    exit(0);
	case 'c':
	{
	    int leng;
	    leng = (s?strlen(s):0) + strlen(*recip) +
	    	strlen(build_arrived_string(0)) + 3;
	    fprintf(stderr,
		"goofey: Total message length is %d which %s\n", leng,
		    (leng>SERVER_MAXREQ)?
	       (autoSplit()?"will result in split": "exceeds maximum"):
		    "is OK!");
	    break;
	}
	case 'w':
	    save_goof(s);
	    break;
	case 'r':
	    read_file(&s, &len);
	    break;
	case '?':
	case 'h':
	    printf(

“~ commands for goofey message editing:\n
~e, ~v: Edit message using editor\n
~p: Print current message\n
~.: End message (don’t use this if you’re using rlogin!).\n
~w: Write message to a file (you will be prompted for a filename)\n
~r: Read from a file or command.\n
~q: Quit, save message in dead.goofey, don’t send\n
~x: Quit, don’t send, don’t save\n
~t: Re-enter recipients (will prompt for recipients)\n
~c: Check message length\n
~h, ~?: Print help\n”
);
break;
default:
fprintf(stderr,
“goofey: Unknown ~ command. Use ~h for help\n”);
break;
}
}
if (prompt && (textlen==0 || (textlen==1 && ln==’.’)))
break; /
Exit if empty line or '.'is entered */
if (!meta) {
RESIZE(s,len,i,ln);
if (extra)
RESIZE(s,len,strlen(extra),extra);
}
} else
break;
}
return s;
}

void
print_help VOIDFUNC
{
puts(“
Usage: goofey [-v] [options]* [command]\n
-v : prints the version number\n
-h : prints help.\n
–width=Width : set terminal width for this client\n
–port=PortNumber : Specify port number where server lives.\n
–machine=MachineName : Specify machine where server lives.\n
–no-blat : request server to send no blat message\n
–only-one : server will only send 1 request per connection\n
–return-response : server will redirect response for pages to caller\n
–inform-replace : will generate GOOF IN messages on client replace\n
–forget-fail : messages that don’t get through are not buffered\n
–tag-success : server will add an extra character for failed messages\n
–format-outgoing : server will line wrap your message for you\n
–shutdown-broke : do not use the ‘shutdown’ system call\n
\n
The server may have further help when it is available\n
”);
}

void
check_alloc DEFUN((v,i),void * v AND int i)
{
if (v==0) {
fprintf(stderr,"\rGoofey: memory allocation (%d bytes) failed!\n",i);
exit(1);
}
}

void
do_resize DEFUN((m,l,x,s), char **m AND int *l AND int x AND char s)
{/
Resize an block of memory */
if (*m)
*m = realloc(*m,(*l)+x+2);
else
*m = malloc(x+2);
check_alloc(*m,*l + x +2);
bcopy(s,(*m)+(*l),x);
*l+=x;
(*m)[*l]=0;
}

#ifdef READLINE
char *
xmalloc (int bytes)
{
char *temp = (char *)malloc (bytes);

check_alloc(temp,bytes);
return (temp);

}

char *
xrealloc (char *pointer, int bytes)
{
char *temp;

if (!pointer)
temp = (char *)xmalloc (bytes);
else
temp = (char *)realloc (pointer, bytes);

check_alloc(temp,bytes);
return (temp);

}
#endif