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/
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/
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.
Just for grins, the Win95 and UNIX versions of networked Maelstrom
are compatible (though not available for general release.)
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)
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…
njhOn Tue, 23 Jun 1998, Sam Lantinga wrote:
Good night.
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:
*
@(#)goofey.c 2.08 Pluto Client code.
after a query. Now prompts for message of none given to -s.
No more static limits! Removed a lot of dead code.
waiting for pluto to come back to life).
#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:
#endif
#ifndef LOCAL_PORT
#endif
#ifndef LOCAL_PORT_MAX
#endif
/* Portability stuff: if we don’t have fd_set or FD_SET,etc macros
typedef struct fd_set { int fds_bits[1]; } fd_set;
#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
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 */
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(),×);
/* 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
if (fcntl(sock,F_SETFL,FNDELAY))
if (fcntl(sock,F_SETFL,O_NONBLOCK))
{
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);
}
/* -------------------------------------------------------------------- *
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