lafe/Makefile0000644000175000017500000000464111275060416011524 0ustar dlddld# $Id: Makefile,v 1.3 2009/11/06 17:38:41 hmless Exp $ # # Makefile for lafe (Latency Free Empire) # # This makefile has specific clauses for target machines. If your machine # isn't listed, first try the "termcap" and "curses" machines. If these # don't work, add a clause with the necessary compile and link flags for # your machine, and send me an email. I hate #ifdefs in the code, and # struggle to eliminate them through compile/link flags. # GNU readline is in the readline/ directory, and uses a configure script. # It works (with the occaisional prod) on the machines I've used. YMMV. # Good luck debugging/porting it if your machine isn't a supported target # of readline. # # -Drake (dld@debian.org) SHELL = /bin/sh SRC = lafe.c alias.c history.c parse.c var.c game.c OBJ = $(SRC:.c=.o) all: @echo "Chose one of the following architectures, or add a new entry to the Makefile:" @echo " termcap curses" @echo " irix irix6 aix solaris24 solaris25 osf1 linux" # # machine-specific targets # linux: $(OBJ) $(CC) $(LDFLAGS) -o lafe $(OBJ) -lreadline -lcurses # the Irix 6 target uses the -32 flag to be runnable on Irix5 irix6: CFLAGS='-g -I. -32 -mips1' LDFLAGS='-g -32 -mips1' make termcap # should work on irix5 or irix6 irix: CFLAGS='-g -I.' LDFLAGS='-g' make termcap # should work on AIX3 or AIX4, posixy aix: CFLAGS='-g -I.' LDFLAGS='-g' make curses # another posixy unix, note the vanilla flags. osf1: CFLAGS='-g -I.' LDFLAGS='-g' make curses # not so posixy, weird libraries solaris24: CFLAGS='-g -I. -DHAVE_CONFIG_H' LDFLAGS='-g -lsocket -lnsl' make termcap solaris25: CFLAGS='-g -I.' LDFLAGS='-g -lsocket -lnsl' make termcap # # Internal lafe targets, CFLAGS and LDFLAGS won't get set if you call these termcap: configure $(OBJ) cd readline ; make $(CC) $(LDFLAGS) -o lafe $(OBJ) -Lreadline -lreadline -ltermcap curses: configure $(OBJ) cd readline ; make $(CC) $(LDFLAGS) -o lafe $(OBJ) -Lreadline -lreadline -lcurses configure: readline/config.status readline/config.status: cd readline ; CFLAGS='$(CFLAGS)' ./configure ChangeLog: cvs2cl clean: -rm -f lafe *.o lafe.tar.gz example.tar.gz distclean: clean rm -f ChangeLog dist: distclean ChangeLog cd .. ; tar cf - --exclude CVS lafe/Makefile lafe/lafe.c lafe/lafe.h lafe/alias.c lafe/parse.c lafe/history.c lafe/var.c lafe/game.c lafe/README lafe/ChangeLog lafe/TODO lafe/example lafe/lafe.6 | gzip -9 >lafe/lafe.tar.gz lafe/lafe.c0000644000175000017500000005062411275056436011150 0ustar dlddld #ifdef LAFEREVISION char *LafeRevision=LAFEREVISION; #else char *LafeRevision="$Revision: 1.3 $"; #endif /* Latency Free Empire This is a client optimized for high latency links. You can type in, edit, and send several commands before the server replies with output from the first command. This allows you to make better use of the existing bandwidth than most other clients. pei and eif are particularly susceptible to latency, as they require a complete exchange of data before prompting again. This could also be called LAg Free Empire. Empire 2 asynch features are supported. Empire 3 C_SYNC isn't supported yet, as this is a rather dumb client (in between emp_client and eif). Readline is supported, and at the moment required. If you want lafe without readline, use emp_client instead. -harmless@empire.net */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* include-file hell... I hate ifdefs... */ #if defined(LINUX) #include #include #else #include #include #endif #include #include #include "lafe.h" /* some defaults in case nothing is specified. Change as you wish. */ #define DEFPORT "6789" #define DEFHOST "blitz.wolfpackempire.com" #define DEFCOUNTRY "visitor" #define DEFPASSWORD "visitor" #ifndef LAFERC #define LAFERC "~/.laferc" #endif int verbose=0; /************************************************************************ * The filedescriptor of the empire server socket. Had to make this * global in order to be accessible to event_hook(). * I always feel guilty when using globals... ************************************************************************/ int s=-1; /************************************************************************ * The stream pointer of the log file. Another global.. ************************************************************************/ FILE *log=NULL; /* process commands before sending to server on socket s. * handles aliases and local commands. Called by execute() and runfeed() * as well. */ extern void parse_command(char *); /* the length of the prompt. This plus rl_point is how far we have to go back to the beginning of the line. */ /* extern int rl_visible_prompt_length; */ /* I'm cheating by using these. */ /* extern int _rl_horizontal_scroll_mode; extern int _rl_vis_botlin, _rl_last_v_pos, _rl_last_c_pos; */ /* pointer to the readline prompt. This can change in the middle of * readline() if a prompt arrives in the middle of typing. */ /* extern char *rl_prompt,*rl_display_prompt; */ /************************************************************************ * Interrupt count, incremented each time an interrupt is received. ************************************************************************/ static int interrupts=0; void interrupt() { interrupts++; } /*************************************************************************** * A few routines to clear the readline-portion of the display. * Some initialization to get the necesary termcap strings and a * couple support routines. ***************************************************************************/ static char *tc_sc, *tc_rc, *tc_cd; /* save, restore, clear-to-eos */ static char *tc_LE, *tc_ce; static char *tc_ch, *tc_UP; static char *tc_so,*tc_se; /* highlighting on and off */ extern char *tgetstr(); /* for those systems that think putchar is a macro ... */ int putcharf(int c) { return putchar(c); } void init_tc() { static char *termstrbuf; char **area; /* SGI and IBM docs say area is meaningless under terminfo emulation, but it crashes without a sizeable buffer on the SGI... */ termstrbuf = malloc(2048); area = &termstrbuf; tgetent(termstrbuf,getenv("TERM")); tc_rc = tgetstr("rc",area); /* restore cursor */ tc_cd = tgetstr("cd",area); /* clear eos */ tc_sc = tgetstr("sc",area); /* save cursor */ tc_LE = tgetstr("LE",area); /* left #1 */ tc_ce = tgetstr("ce",area); /* clear to eol */ tc_ch = tgetstr("ch",area); /* set horzontal cursor position */ tc_UP = tgetstr("UP",area); /* up #1 */ tc_se = tgetstr("se",area); /* turn off highlighting */ tc_so = tgetstr("so",area); /* turn on highlighting */ /* this section is just for debugging termcap capabilities */ #if 0 if (tc_sc) fprintf(stderr,"tc: save cursor\n"); else fprintf(stderr,"tc: no save cursor\n"); if (tc_sc) fprintf(stderr,"tc: set horizontal cursor\n"); else fprintf(stderr,"tc: no set horizontal cursor\n"); if (tc_rc) fprintf(stderr,"tc: restore\n"); else fprintf(stderr,"tc: no restore\n"); if (tc_cd) fprintf(stderr,"tc: clear to end of screen\n"); else fprintf(stderr,"tc: no clear to end of screen\n"); if (tc_LE) fprintf(stderr,"tc: left param\n"); else fprintf(stderr,"tc: no left param\n"); if (tc_ce) fprintf(stderr,"tc: clear to end of line\n"); else fprintf(stderr,"tc: no clear to end of line\n"); #endif } /* move to left edge and clear to end */ void clear_readline() { if (tc_ch) { /* extra 0 added to tparm() so tgoto() can be used interchangeably */ tputs(tparm(tc_ch,0,0),1,putcharf); #if 0 } else if (tc_LE) { tputs(tparm(tc_LE,_rl_last_c_pos,0),1,putcharf); #endif } else if (tc_rc) { tputs(tc_rc,1,putcharf); } else { putcharf('\r'); } #if 0 if (_rl_last_v_pos>0) { if (tc_UP) { tputs(tparm(tc_UP,_rl_last_v_pos,0),1,putcharf); } else { /* couldn't reach beginning, the display will look weird */ } } #endif if (tc_cd) { tputs(tc_cd,1,putcharf); } else if (tc_ce) { tputs(tc_ce,1,putcharf); /* may not have cleared whole screen */ } else { printf("\n"); } } /* save position in case we need to clear */ void save_readline() { if (tc_sc) { tputs(tc_sc,1,putcharf); } } void quit() { rl_cleanup_after_signal(); exit(0); } /******************************************************** * Maximum number of commands on the output stack ********************************************************/ FILE *out; /* current prompt mode. Will be actual only if commands==0. */ char mode='\000'; static char *prompt=NULL; static int timeleft,btus,teles=0; /************************************************************ * Handle input lines depending on protocol number ************************************************************/ void process_line(char *buffer,int len) { int i,j,k; if (len<3) return; switch (buffer[0]) { case 'a': case '3': fwrite(buffer+2,1,len-2,out); if (log) fwrite(buffer+2,1,len-2,log); quit(); break; case '1': /* if out is the terminal, attempt to send highlighting codes */ if (out==stdout && tc_so && tc_se) { int i,lit=0; for (i=2;i3) { teles=1; sscanf(buffer+2,"%d new teles",&teles); putchar('\07'); /* BUG: prompt may not be a command prompt */ if (prompt) free(prompt); prompt=malloc(len+30); if (!commands) { sprintf(prompt,"%s [%d:%d] Command: ",buffer+2,timeleft,btus); } else { sprintf(prompt,"%s : ",buffer+2); } /* cheated a bit here again.. this isn't supposed to be allowed */ /* rl_display_prompt=rl_prompt=prompt; */ rl_set_prompt(prompt); /* rl_redisplay(); */ #if 0 rl_visible_prompt_length = rl_expand_prompt(rl_prompt); #endif } else { teles=0; } break; case '4': case '5': mode=buffer[0]; if (commands>1) { buffer[len-2]=0; fprintf(stdout,"%s %s\n",buffer+2,commandline[1]); if (log) fprintf(log,"%s %s\n",buffer+2,commandline[1]); } else { if (log) fwrite(buffer+2,1,len-3,log); } if (commands>0 && commandline[0]) { free(commandline[0]); } for (i=1;i0) commands--; if (prompt) free(prompt); prompt=malloc(len); memcpy(prompt,buffer+2,len-3); prompt[len-3]=' '; prompt[len-2]=0; if (!commands) { /* rl_display_prompt=rl_prompt=prompt; */ rl_set_prompt(prompt); } else { /* rl_display_prompt=rl_prompt=": "; */ rl_set_prompt(": "); } #if 0 rl_visible_prompt_length = rl_expand_prompt(rl_prompt); #endif break; case '6': mode=buffer[0]; if (out!=stdout) { fclose(out); /* if pipe */ wait(NULL); } out=stdout; sscanf(buffer+2,"%d %d",&timeleft,&btus); if (commands>1) { fprintf(stdout,"[%d:%d] Command: %s\n",timeleft,btus,commandline[1]); if (log) fprintf(log,"[%d:%d] Command: %s\n",timeleft,btus,commandline[1]); } else { if (log) fprintf(log,"[%d:%d] Command: ",timeleft,btus); } if (commands>0 && commandline[0]) { free(commandline[0]); } for (i=1;i0) commands--; if (prompt) free(prompt); prompt=malloc(len+30); sprintf(prompt,"[%d:%d] Command: ",timeleft,btus); /* cheated a bit here again.. this isn't supposed to be allowed */ if (!commands) { rl_set_prompt(prompt); /* rl_redisplay(); */ /* rl_display_prompt=rl_prompt=prompt; */ } else { /* rl_display_prompt=rl_prompt=": "; */ rl_set_prompt(": "); rl_redisplay(); } /* rl_visible<_prompt_length = rl_expand_prompt(rl_prompt); */ break; case '8': for (i=2;(buffer[i]=='>') && i3) { out = fopen(buffer+j,"a"); } else { out = fopen(buffer+j,"w"); } if (!out) { perror("fopen"); out=stdout; } break; case '9': for (i=2;(buffer[i]=='|' || isspace(buffer[i])) && i1) { printf("Exitting on multiple interrupts\n"); rl_cleanup_after_signal(); exit(-1); } else if (interrupts) { write(s,"aborted\n",8); printf("^C\n"); } else { if (verbose) perror("select"); } } if (i>0) { interrupts=0; /* typing or receiving any data clears the interrupt count */ if (FD_ISSET(s,&in)) { int received=0; clear_readline(); fflush(rl_outstream); while ( (len=recv(s,buffer+offset,sizeof(buffer)-offset,0)) > 0) { received+=len; offset=output(buffer,len+offset); } if (received==0) { if (errno==ENOENT) printf("Lost connection to server\n"); else perror("recv"); quit(); } if (errno==EAGAIN) { /* no worries */ } else { perror("recv2"); quit(); } rl_forced_update_display(); } } } /*************************************************************************** * similar to event_hook. Keeps reading data until the connection is * resynchronized. Call before running a tool that needs complete data * (cmvr's and the like). ***************************************************************************/ void waitsync(char *dummy) { fd_set in; int i; int len; static char buffer[2048]; static int offset=0; /* unprocessed remnants in buffer */ while (commands>0) { /* wait for something from s */ FD_ZERO(&in); FD_SET(s,&in); i=select( FD_SETSIZE,&in,NULL,NULL,NULL); if (i<=0) { if (interrupts>1) { printf("Exitting on multiple interrupts\n"); rl_cleanup_after_signal(); exit(-1); } else if (interrupts) { write(s,"aborted\n",8); printf("^C\n"); } else { if (verbose) perror("select"); } } if (i>0) { interrupts=0; /* typing or receiving any data clears the interrupt count */ if (FD_ISSET(s,&in)) { int received=0; while ( (len=recv(s,buffer+offset,sizeof(buffer)-offset,0)) > 0) { received+=len; offset=output(buffer,len+offset); } if (received==0) { if (errno==ENOENT) printf("Lost connection to server\n"); else perror("recv"); quit(); } if (errno==EAGAIN) { /* no worries */ } else { perror("recv2"); quit(); } } } } } /************************************************************ * Open a socket to host/port and return filedescriptor. ************************************************************/ int empconnect(char *host,int port,char *user,char *country,char *pass) { struct hostent *hp; struct sockaddr_in sin; char buffer[1024]; if (isdigit(*host)) { sin.sin_addr.s_addr = inet_addr(host); } else { hp = gethostbyname(host); if (hp == NULL) { fprintf(stderr, "%s: No such host\n", host); return 0; } memcpy( &sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); } sin.sin_port = htons(port); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("socket:"); return -1; } sin.sin_family = AF_INET; if (connect(s, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("connect"); return -1; } fcntl(s,F_SETFL,FNONBLOCK); if (!user) user="nobody"; sprintf(buffer,"user %s\ncoun \"%s\"\npass %s\nclient lafe %s\nplay\n", user,country,pass,LafeRevision); write(s,buffer,strlen(buffer)); return s; } /*********************************************************************** * Set up readline() and socket s to server. Push each new line * down the socket. ***********************************************************************/ int main(int argc, char **argv) { char *line; char *host=NULL,*port=NULL,*country=NULL,*password=NULL,*logfile=NULL; out = stdout; rl_readline_name = argv[0]; rl_event_hook = (Function *)event_hook; rl_startup_hook = (Function *)save_readline; init_tc(); signal(SIGPIPE,SIG_IGN); /* don't worry if a destination pipe closes */ signal(SIGINT,interrupt); execute(LAFERC); if (argc>=5) { fprintf(stderr,"arguments may be visible\nmodifying your .laferc is the prefered method\n"); country=strdup(argv[1]); password=strdup(argv[2]); host=strdup(argv[3]); port=strdup(argv[4]); if (argc>5) logfile=strdup(argv[5]); else logfile=NULL; } else if (argc>=3) { fprintf(stderr,"arguments may be visible\nmodifying your .laferc is the prefered method\n"); country=strdup(argv[1]); password=strdup(argv[2]); host=getenv("EMPIREHOST"); port=getenv("EMPIREPORT"); if (argc>3) logfile=strdup(argv[3]); else logfile=NULL; } else if (argc==2) { int i; i = lookup_game(argv[1]); if (i>=0) { country = game[i].country; password = game[i].password; host = game[i].host; port = game[i].port; logfile = game[i].logfile; if (game[i].directory) { if (chdir(game[i].directory)) { perror(game[i].directory); } } } else { fprintf(stderr,"%s not found in addgame database. Edit ~/.laferc.\n", argv[1]); } } else if (argc==1) { fprintf(stderr,"environment variables may be visible\nmodifying your .laferc is the prefered method\n"); country=getenv("COUNTRY"); /* not advised for security reasons */ password=getenv("REPRESENTATIVE"); host=getenv("EMPIREHOST"); port=getenv("EMPIREPORT"); logfile=NULL; } else { fprintf(stderr,"Usage:\ \t%s host port country password [logfile]\n\ \t%s host port [logfile]\n\ \t%s [logfile]\n\ environment variables: EMPIREHOST EMPIREPORT COUNTRY REPRESENTATIVE\n", argv[0],argv[0],argv[0]); } if (!host) { host=DEFHOST; fprintf(stderr,"host defaulting to %s\n",host); } if (!port) { port=DEFPORT; fprintf(stderr,"port defaulting to %s\n",port); } if (!country) { country=DEFCOUNTRY; fprintf(stderr,"country defaulting to %s\n",country); } if (!password) { password=DEFPASSWORD; fprintf(stderr,"password defaulting to %s\n",password); } if (logfile) { log = fopen(logfile,"a"); if (!log) perror("log fopen"); } s=empconnect(host,atoi(port),getenv("USER"),country,password); if (s<0) { fprintf(stderr,"Unable to log in to %s at %s/%s\n",country,host,port); exit(-1); } /* clear command arguments. Doesn't work on all systems. */ { int i,j; for (i=0;i extern int command_stack; extern int commands; extern char **commandline; extern int s; /* server socket */ extern FILE *out; /* current redirection */ extern FILE *log; /* log file */ extern char mode; /* current prompt mode. Correct only if commands==0. */ extern int verbose; /* whether to spit out all the messages or not */ /* global routines in lafe */ extern char *expand_tilde(char *path); extern char *expand_alias(char *); extern char *getvar(char *); extern void putvar(char *,char *); extern char *expand_variables(char *); extern void parse_command(char *); extern void queue_command(char *); /* local commands */ extern void clearvar(char *); extern void setvar(char *); extern void history(char *); extern void alias(char *); extern void execute(char *); extern void runfeed(char *); extern void shell(char *); extern void help(char *); extern void addgame(char *); extern void verbosity(char *); extern void waitsync(char *); /* game database */ struct game { char *name; char *country; char *password; char *host; char *port; char *logfile; char *directory; }; extern int games; extern struct game *game; /* global routine in game.c */ int lookup_game(char *); lafe/alias.c0000644000175000017500000000746310350273731011325 0ustar dlddld /* alias.c * * Alias expansion and maintenance for lafe. */ #include #include #include #include "lafe.h" static int aliases=0; static char **key=NULL; /* alias names */ static char **value=NULL; /* alias definitions */ static int width=0; /* size of largest alias key */ static void print_aliases() { int i; for (i=0;i=0) { free(key[i]); free(value[i]); aliases--; key[i]=key[aliases]; value[i]=value[aliases]; } else { printf("%s not defined\n",name); } } static void define_alias(char *name,char *definition) { int i; int w; w=strlen(name); if (w>width) width=w; i=find_alias(name); if (i>=0) { free(value[i]); value[i]=strdup(definition); } else { key=realloc(key,(aliases+1) * sizeof(*key)); value=realloc(value,(aliases+1) * sizeof(*value)); key[aliases]=strdup(name); value[aliases]=strdup(definition); aliases++; } } /* alias expansion. Tokenizes arg and adds tokens to environment $1 $2 .... */ /* not finished */ static void tokenize(char *arg) { int start,end; char c,buffer[8]; int i; putvar("*",arg); /* put each fragment of arg into environment variables */ i=0; for (start=0;arg[start];start++) { if (!isspace(arg[start])) { for (end=start+1;arg[end] && !isspace(arg[end]);end++) ; c=arg[end]; arg[end]=0; sprintf(buffer,"%d",++i); putvar(buffer,arg+start); arg[end]=c; start=end; } } sprintf(buffer,"%d",++i); clearvar(buffer); } /* line must be individually malloced. If the alias is expanded, line will be freed by expand_alias, and a new line returned */ char *expand_alias(char *line) { int i,j,k; char *alias,*semi; for (i=0;line[i] && isspace(line[i]);i++) ; for (j=i;line[j] && !isspace(line[j]);j++) ; /* attempt to locate the alias index for this line */ { char c; c=line[j]; line[j]=0; k=find_alias(line+i); line[j]=c; } /* no alias found, so return line */ if (k<0) return line; /* expand ;'s in alias */ alias=strdup(value[k]); semi=strchr(alias,';'); while (semi) { *semi='\n'; semi=strchr(semi+1,';'); } /* advance j to next character */ for (;line[j] && isspace(line[j]);j++) ; /* if there are $'s in the alias, fill variables and return alias value */ if (strchr(alias,'$') || strchr(alias,'\n')) { tokenize(line+j); free(line); return alias; /* otherwise, replace the first word and append arguments */ } else { int len; char *newline; len=strlen(alias); newline=malloc(len+strlen(line+j)+2); strcpy(newline,alias); newline[len]=' '; strcpy(newline+len+1,line+j); free(line); free(alias); return newline; } } /* no arguments prints the alias database. * one argument delete the alias for that argument. * two arguments defines a new alias. */ void alias(char *line) { int i,j,k; i=0; /* print alias database */ if (!line || !*line) { print_aliases(); return; } /* alias name starts at i, ends at j. Def starts at k. */ /* get name of new or old alias */ for (j=i;line[j] && !isspace(line[j]);j++) ; /* skip whitespace */ for (k=j;line[k] && isspace(line[k]);k++) ; /* delete definition of name */ if (!line[k]) { line[j]=0; remove_alias(line+i); return; } line[j]=0; define_alias(line+i,line+k); } lafe/parse.c0000644000175000017500000001413010350273731011333 0ustar dlddld #include #include #include #include #include #include "lafe.h" int commands=0; int command_stack=0; char **commandline=NULL; /* replace a leading tilde by home directory */ char *expand_tilde(char *path) { int len; char *buffer; if (path && path[0]=='~') { len = strlen(getenv("HOME")) + strlen(path); buffer=malloc(len); sprintf(buffer,"%s%s",getenv("HOME"),path+1); free(path); return buffer; } else { return path; } } void verbosity(char *arg) { if (!strcmp("on",arg)) { verbose=1; } else if (!strcmp("off",arg)) { verbose=0; } else { verbose ^= 1; } if (verbose) printf("Verbose On\n"); else printf("Verbose Off\n"); } void shell(char *arg) { if (system(arg)) perror("system"); } /* lines limited to size of buffer. Shouldn't be much of a problem. */ void runfeed(char *arg) { FILE *pipe; char buffer[4096]; char *end; int fails=0; pipe = popen(arg,"r"); if (!pipe) { perror("popen"); return; } buffer[sizeof(buffer)-1]=0; while (fgets(buffer,sizeof(buffer)-1,pipe) != NULL) { end=strchr(buffer,'\n'); if (end) { *end = 0; parse_command(buffer); } else { fails++; } } if (fails) fprintf(stderr,"%d lines too long\n",fails); pclose(pipe); } void execute(char *arg) { FILE *fp; int i; char buffer[4096]; char *end; int fails=0; char *filename; buffer[sizeof(buffer)-1]=0; filename = strdup(arg); /* truncate anything past first argument */ for (i=0;filename[i] && !isspace(filename[i]);i++) ; filename[i]=0; /* expand a leading ~ into $HOME */ filename = expand_tilde(filename); fp = fopen(filename,"r"); if (!fp) { perror(filename); free(filename); return; } while (fgets(buffer,sizeof(buffer)-1,fp) != NULL) { end=strchr(buffer,'\n'); if (end) { *end = 0; parse_command(strdup(buffer)); } else { fails++; } } if (fails) fprintf(stderr,"%d lines too long\n",fails); fclose(fp); free(filename); } /************************************************************************** * parse commands. Look for commands matching local commands * and call the local handler. All others are sent directly to * the server on socket s. **************************************************************************/ struct local_command { char *name; /* command name .. "execute" */ int shortest; /* shortest abbreviation */ void (*process)(); /* function to parse arguments */ char *description; /* short description */ }; /* should be in alphabetical order */ static struct local_command local_command[] = { { "addgame", 7, addgame, "game country password host port logfile directory" }, { "alias", 5, alias, "[NAME [DEFINITION]]" }, { "execute", 4, execute, "FILENAME" }, { "help", 4, help, "" }, { "history", 4, history, "" }, { "runfeed", 7, runfeed, "SHELL_COMMAND" }, { "setvar", 4, setvar, "[NAME [VALUE]]" }, { "shell", 5, shell, "SHELL_COMMAND" }, { "verbose", 5, verbosity, "[on|off]" }, { "waitsync", 5, waitsync, "" } }; void help(char *dummy) { int i; extern char *LafeRevision; printf("\ \t\tLatency Free Empire\t\n\ \t\t %s\n\ \n\ \tusage: lafe [country [password [host [port]]]]\n\ \nlocal commands:\n",LafeRevision); for (i=0;i0 || mode=='6' || !mode) { line=expand_alias(line); line=expand_variables(line); } if (strchr(line,'\n')) { parse_command(line); } else { loc=pos=0; while (loc=local_command[loc].shortest) { int arg; /* position of arguments */ for (arg=pos;line[arg] && !isspace(line[arg]);arg++) ; for (;line[arg] && isspace(line[arg]);arg++) ; local_command[loc].process(line+arg); free(line); return; } } else if (line[start+pos] > local_command[loc].name[pos]) { loc++; pos=0; } else { break; } } } } /* no match found, send to server if connected to server */ if (s != -1) { len=strlen(line); line[len]='\n'; if (write(s,line,len+1) != len+1) { perror("command write failure"); } line[len]=0; if (!commands) { if (log) fprintf(log,"%s\n",line); } if (commands > command_stack-1) { if (!commandline) { command_stack=10; commandline=malloc(sizeof(*commandline)*command_stack); } else { command_stack *= 2; commandline = realloc(commandline,sizeof(*commandline)*command_stack); } } if (!commandline) { fprintf(stderr, "Memory allocation failure, commandlines lost\n"); command_stack=0; commands=0; free(line); } else { commandline[commands++]=line; } } else { if (verbose) fprintf(stderr,"# %s\n",line); free(line); } } lafe/history.c0000644000175000017500000000042510350273731011724 0ustar dlddld /* interface to the history library */ #include #include "readline/history.h" void history(char *line) { int i; HIST_ENTRY **entry; entry = history_list(); for (i=0;iline); } } lafe/var.c0000644000175000017500000000533010350273731011013 0ustar dlddld/* var.c * * Variable expansion and maintenance. * * All variables are stored as enviroment variables. */ #include #include #include #include #include #include "lafe.h" extern char **environ; static void printvar() { int i; for (i=0;environ[i];i++) { printf("%s\n",environ[i]); } } char *getvar(char *name) { char *v; v = getenv(name); if (v) return strdup(v); else return NULL; } void putvar(char *name,char *val) { setenv(name,val,1); } void clearvar(char *name) { unsetenv(name); } /* variable expansion, return a malloced line */ char *expand_variables(char *line) { char *oldline; int size; int src,dst,i; char *var,*val; /* exit fast if no $ in line */ /* if (!strchr(line,'$')) return line; */ oldline=line; size=256; line=malloc(size); if (!line) { perror("malloc"); return oldline; } for (src=dst=0;oldline[src];) { /* begining of a variable */ if (oldline[src] == '$') { /* allocate var to be the variable name */ /* a general {} variable */ if (oldline[++src]=='{') { for (i=(++src);oldline[i] && oldline[i]!='}';i++) ; var=malloc(i-src+1); strncpy(var,oldline+src,i-src); var[i-src]=0; src=i+1; /* a simple alphanumeric variable */ } else if (isalnum(oldline[src])) { for (i=src;isalnum(oldline[i]);i++) ; var=malloc(i-src+1); strncpy(var,oldline+src,i-src); var[i-src]=0; src=i; /* single character variable */ } else { var=malloc(2); var[0]=oldline[src]; var[1]=0; src++; } /* get value and copy into line at point dst */ val=getvar(var); if (!val) { if (verbose) fprintf(stderr,"%s not defined\n",var); } else { for (;*val;val++) { line[dst++] = *val; if (dst>=size-1) { line=realloc(line,size*=2); } } } free(var); /* everything between '' is returned verbatim, without the quotes. * needed to define aliases with runtime variables */ } else if (oldline[src]=='\'') { for (src++;oldline[src] && oldline[src]!='\'';src++) { line[dst++]=oldline[src]; if (dst>=size-1) { line=realloc(line,size*=2); } } if (oldline[src]) src++; /* copy character from src to dst */ } else { line[dst++]=oldline[src++]; if (dst>=size-1) { line=realloc(line,size*=2); } } } /* terminate string */ line[dst]=0; free(oldline); return line; } /* local commands to affect variables */ void setvar(char *line) { int i,j; for (i=0;line[i] && !isspace(line[i]);i++) ; if (!line[i]) { printvar(); return; } for (j=i;line[j] && isspace(line[j]);j++) ; line[i]=0; if (line[j]) putvar(line,line+j); else clearvar(line); } lafe/game.c0000644000175000017500000000372410700147475011145 0ustar dlddld/************************************************************************* * parse the addgame command, and store in a database for later lookup *************************************************************************/ #include #include #include #include "lafe.h" struct game *game=NULL; int games=0; /* extract the next argument from an addgame commandline */ static char *token(char **str) { char *t=NULL; int i; while (**str && isspace(**str)) (*str)++; if (! **str) return NULL; if (**str=='"') { (*str)++; for (i=0;(*str)[i]!='"' && (*str)[i];i++) ; } else { for (i=0;!isspace( (*str)[i] ) && (*str)[i];i++) ; } t = malloc(i+1); strncpy(t,*str,i); t[i]=0; (*str) += i; if (**str) (*str)++; return t; } void addgame(char *arg) { if (!game) { game=malloc(sizeof(*game)); } else { game = realloc(game,(games+1)*sizeof(*game)); } memset(game+games,0,sizeof(*game)); game[games].name = token(&arg); if (game[games].name) { game[games].country = token(&arg); game[games].password = token(&arg); game[games].host = token(&arg); game[games].port = token(&arg); game[games].directory = expand_tilde(token(&arg)); game[games].logfile = expand_tilde(token(&arg)); if (game[games].country && game[games].password && game[games].host && game[games].port) { games++; } else { printf("addgame requires country password host and port parameters\n"); } } else { int i; for (i=0;i.delta ; com * >>.delta ; prod * >>.delta ; waitsync ; shell delta .delta' alias cdelta 'cen * >.cdelta ; prod * >>.cdelta ; waitsync; shell delta .cdelta' # # Multi-line aliases with variable expansion # Stolen from example.peirc (in the pei distribution) # Set thresholds: alias thom 'th o * ?newd=o 1; th i * ?newd=m 1; th d * ?newd=g 1; th l * ?newd=j 1; th h * ?newd=k 1; th s * ?newd=i 1; th g * ?newd=d 1; th p * ?newd=% 1; th r * ?newd=u 1' # Build a bridge and explore onto it: alias bb 'build b $1 $2 ; expl c $1 1 $2 ; ' # fire alias fi 'fire sect $1 $2' alias fl 'fire land $1 $2' alias fs 'fire ship $1 $2' # machine-gun fire alias mfi 'fi $1 $2 ; fi $1 $2 ; fi $1 $2 ; fi $1 $2 ; fi $1 $2 ; fi $1 $2 ; fi $1 $2 ; fi $1 $2 ' alias mfl 'fl $1 $2 ; fl $1 $2 ; fl $1 $2 ; fl $1 $2 ; fl $1 $2 ; fl $1 $2 ; fl $1 $2 ; fl $1 $2 ' alias mfs 'fs $1 $2 ; fs $1 $2 ; fs $1 $2 ; fs $1 $2 ; fs $1 $2 ; fs $1 $2 ; fs $1 $2 ; fs $1 $2 ' # Production of interesting sectors: alias pr 'prod * ?newd#m&newd#g&newd#o&newd#a&newd#+' # Pipe info pages to more: alias info 'info $1 | more' # Move commodities out of sectors they shouldn't be in: alias thod 'th i * ?iron>1&newd#j&newd#k&newd#w 1; th o * ?oil>1&newd#%&newd#d&newd#t&newd#r&newd#w 1; th d * ?dust>1&newd#b&newd#t&newd#r&newd#w 1; th l * ?lcm>1&newd#h&newd#*&newd#!&newd#t&newd#r&newd#i&newd#d&newd#l&newd#p&newd#w 1; th h * ?hcm>1&newd#h&newd#*&newd#!&newd#i&newd#d&newd#w 1; th b * ?bar>1&newd#b&newd#w 1; th d * ?newd=b&bar=999 1' # # Several blitz games defined using variables # Only directory is specified, there is no logfile for these. # setvar WOLFDIR ~/emp/wolf setvar VAMPIREHOST idiot.alfred.edu setvar VAMPIREPORT 6667 addgame v1 1 1 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v2 2 2 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v3 3 3 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v4 4 4 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v5 5 5 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v6 6 6 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v7 7 7 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v8 8 8 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v9 9 9 $VAMPIREHOST $VAMPIREPORT $WOLFDIR addgame v10 10 10 $VAMPIREHOST $VAMPIREPORT $WOLFDIR setvar CHEETAHHOST empire.net setvar CHEETAHPORT 6667 addgame c1 1 1 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c2 2 2 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c3 3 3 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c4 4 4 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c5 5 5 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c6 6 6 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c7 7 7 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c8 8 8 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c9 9 9 $CHEETAHHOST $CHEETAHPORT $WOLFDIR addgame c10 10 10 $CHEETAHHOST $CHEETAHPORT $WOLFDIR setvar HOWITZERDIR ~/emp/how setvar HOWITZERHOST empire.net setvar HOWITZERPORT 7778 addgame h1 1 1 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h2 2 2 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h3 3 3 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h4 4 4 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h5 5 5 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h6 6 6 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h7 7 7 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h8 8 8 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h9 9 9 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR addgame h10 10 10 $HOWITZERHOST $HOWITZERPORT $HOWITZERDIR # visitor accounts, no directory or logfile addgame hv visitor visitor empire.net 7778 addgame bv visitor visitor measun10.city.ac.uk 7778 addgame gv Visitor visitor isumataq.eskimo.com 13715 addgame vv visitor visitor idiot.alfred.edu 6667 addgame cv visitor visitor empire.net 6667 # a long term game # routed to a localhost where an empirehub is running, # connecting to dork.alfred.edu/3777, listening to 3777 on valera # all transactions will be stored in ~/emp/mini/log addgame mini harmless DntUWsh localhost 3777 ~/emp/mini log lafe/example/build0000644000175000017500000000031310350273731012530 0ustar dlddldbuild sh * ?des=h&avail>83&lcm>39&hcm>19 ts2 build sh * ?des=h&avail>503&lcm>239&hcm>119 ts2 2 build sh * ?des=h&avail>923&lcm>439&hcm>219 ts2 3 load c ~ ?type=ts2&civ=0 50 load f ~ ?type=ts2&food=0 10 lafe/example/order0000644000175000017500000000363610350273731012557 0ustar dlddldfleet S ~ ?xloc=15&yloc=-3&type=ts2&civ=50&autonav=0&food>0 fleet S ~ ?xloc=14&yloc=-2&type=ts2&civ=50&autonav=0&food>0 fleet S ~ ?xloc=4&yloc=-4&type=ts2&civ=50&autonav=0&food>0 fleet O ~ ?xloc=9&yloc=5&type=ts2&civ=50&autonav=0&food>0 fleet O ~ ?xloc=4&yloc=4&type=ts2&civ=50&autonav=0&food>0 fleet O ~ ?xloc=1&yloc=3&type=ts2&civ=50&autonav=0&food>0 fleet O ~ ?xloc=-2&yloc=0&type=ts2&civ=50&autonav=0&food>0 fleet O ~ ?xloc=-5&yloc=-5&type=ts2&civ=50&autonav=0&food>0 fleet O ~ ?xloc=-5&yloc=-3&type=ts2&civ=50&autonav=0&food>0 fleet O ~ ?xloc=0&yloc=2&type=ts2&civ=50&autonav=0&food>0 fleet C ~ ?xloc=-5&yloc=-1&type=ts2&civ=50&autonav=0&food>0 fleet C ~ ?xloc=-6&yloc=-2&type=ts2&civ=50&autonav=0&food>0 fleet C ~ ?xloc=-7&yloc=-3&type=ts2&civ=50&autonav=0&food>0 fleet C ~ ?xloc=-8&yloc=-4&type=ts2&civ=50&autonav=0&food>0 fleet L ~ ?xloc=-4&yloc=-2&type=ts2&civ=50&autonav=0&food>0 fleet L ~ ?xloc=20&yloc=-2&type=ts2&civ=50&autonav=0&food>0 fleet L ~ ?xloc=19&yloc=-5&type=ts2&civ=50&autonav=0&food>0 fleet L ~ ?xloc=16&yloc=0&type=ts2&civ=50&autonav=0&food>0 fleet L ~ ?xloc=26&yloc=-2&type=ts2&civ=50&autonav=0&food>0 fleet L ~ ?xloc=2&yloc=-4&type=ts2&civ=50&autonav=0&food>0 fleet L ~ ?xloc=5&yloc=-3&type=ts2&civ=50&autonav=0&food>0 fleet X ~ ?xloc=-23&yloc=-7&type=ts2&civ=50&autonav=0&food>0 fleet X ~ ?xloc=-22&yloc=-8&type=ts2&civ=50&autonav=0&food>0 fleet X ~ ?xloc=-25&yloc=-7&type=ts2&civ=50&autonav=0&food>0 fleet A ~ ?xloc=14&yloc=2&type=ts2&civ=50&autonav=0&food>0 order C d 34,22 - order L d -17,19 - order O d 42,-22 - order S d -36,12 - order X d 22,12 - order A d -46,20 - order S ?xloc=14&yloc=-2&type=ts2&civ=50&autonav#0&food>0 d -6,-2 - order S ?xloc=15&yloc=-3&type=ts2&civ=50&autonav#0&food>0 d -6,-2 - order S ?xloc=4&yloc=-4&type=ts2&civ=50&autonav#0&food>0 d -6,-2 - order L ?xloc=20&yloc=-2&type=ts2&civ=50&autonav#0&food>0 d -11,-1 - order L ?xloc=19&yloc=-5&type=ts2&civ=50&autonav#0&food>0 d -11,-1 - lafe/example/README0000644000175000017500000000137110350273731012373 0ustar dlddldexample.laferc A sample .laferc. Game connectivity info and common aliases are defined here. Customize to your liking and copy into your home directory as .laferc. nocap A sample exec script that fluxes your cap (so it can't be sacked). Assumes sector 22,-4 is less that 5% efficient. build Build and load trade ships in harbors with lots of avail. Not a smart script, it overbuilds. Be sure to build your warships first. order An example exec script to assign sailing orders to trade ships based on origin. Puts each destination in a separate fleet, and assigns temporary orders to route around hazards. scuttle An example exec script to nav trade ships the last bit to destination harbors, and scuttle 100% trade ships once there. lafe/lafe.60000644000175000017500000000662011275052357011066 0ustar dlddld.TH lafe 6 .\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection .\" other parms are allowed: see man(7), man(1) .SH NAME lafe \- Latency Free Empire client .SH SYNOPSIS .B lafe .I "country [password [host [port]]]" .SH "DESCRIPTION" .BR lafe is an empire client optimized for high latency links. You can type in, edit, and send several commands before the server replies with output from the first command. This allows you to make better use of the existing bandwidth than most other clients. .B pei and .B eif are particularly susceptible to latency, as they require a complete exchange of data before prompting again. .B lafe could also be called LAg Free Empire. Empire 2 asynch features are supported. Empire 3 C_SYNC isn't supported, as this is a rather dumb client (in between emp_client and eif) and C_SYNC was removed from Empire 4 (Wolfpack). Readline is supported, and at the moment required. If you want .B lafe without readline, use .B emp_client instead. .SS "Parameters" .PD .TP 10 .TP .B country If the only parameter this is use as a key to look up the game name as specified by the addgame commands in the $HOME/.laferc file. Otherwise it is used as the country name on the server. Defaults to 'visitor' if unspecified. .TP .B password defaults to 'visitor' if unspecified. .TP .B host defaults to 149.84.128.9 if unspecified. .TP .B port defaults to 6667 if unspecified. .PD 0 .SS "Local commands" .PD .TP 10 .TP .B help print syntax summary of local commands. .TP .B addgame game country password host port logfile directory .br add a game definition for the "lafe game" startup method. .TP .B execute Handles exec scripts locally .TP .B runfeed Runs the argument as a shell command, and sends the output to the server shell Run the argument as a shell script .TP .B alias Print, delete, or define an alias. Use single quotes around the alias definition to protect $*, $1, $2, ... .TP .B setvar Set, delete, or print local variables. .TP .B shell Run parameters in a subshell. .TP .B history print the previous commands. .TP .B verbose toggle verbosity of message. "verbose on" if you want all the error messages, C_SYNC messages, etc. Off by default. .TP .B waitsync Wait for all responses from server before proceeding. Useful in scripts before a runfeed or shell command. .PD 0 .SH ENVIRONMENT Environment variables are supported for backward compatibility with .B emp_client. This information is best placed in the $HOME/.laferc file using the addgame command. .PD .TP 16 .B EMPIREHOST hostname of server .TP .B EMPIREPORT TCP/IP port number of server .TP .B COUNTRY Country name to log in as .TP .B REPRESENTATIVE Password for country .SH FILES .PD .TP 14 .B $HOME/.laferc contains any valid local commands to run before connecting to a server. .SH "SEE ALSO" emp_server(6), emp_client(6), emp_hub(6), pei(6), eif(6), http://wolfpackempire.com/ .SH BUGS Output redirection does not work with aliases and internal commands. Simultaneous connection to multiple servers is not yet supported. Version 1.0 addresses both of these issues, but has stalled due to lack of time. .PP When typing well ahead of the server, lafe must guess whether a local command should be parsed locally or forwarded to the server (as part of a telegram for instance). It guesses local, which is not always correct. This is the price you pay for speed. .SH AUTHOR This manual page was written by Drake Diedrich