#!/usr/local/bin/lpc -Cstay #pragma all_inline #include <arpa/telnet.h> #include <socket.h> #include <socket_errors.h> #define DEBUG 1 string file; string address; string name; string password; int auto_login=300; static int socket; void stdout(string foo); void stdin(string s); void login(); void NULL() {} void m_stdin(string s) { s=replace(s,({",","\\n"}),({"\n","\n"})); map_array(s/"\n",stdin); } string my_gets() { string s; s=gets(); return s[0..strlen(s)-2]; } void save() { if(!strlen(file)) return; write("msh: saving....\n"); rm(file); write_file(file,save_object()); popen("chmod 600 "+file); } static mapping movement_verbs=([ "n":"s", "s":"n", "e":"w", "w":"e", "u":"d", "d":"u", "nw":"se", "ne":"sw", "se":"nw", "sw":"ne", "north":"south", "south":"north", "east":"west", "west":"east", "up":"down", "down":"up", "northwest":"southeast", "northeast":"southwest", "southeast":"northwest", "southwest":"northeast", "enter mirror":"climb", "enter ship":"ashore", "pull plug":"up", "enter tree":"out", "aft":"forward", "forward":"aft", "out":"in", "in":"out", ]); void send(string s) { socket_write(socket,s+"\n"); #if DEBUG write("sending: "+code_value(s)+"\n"); #endif } /* from-to database */ mapping dbase=([]); void remove_entry(string from,string to) { if(dbase[to]) dbase[to]=m_delete(dbase[to],from); } void add_entry(string from,string to,string how) { #if DEBUG write("msh: From:"+from+" to:"+to+" "+how+"\n"); #endif if(dbase[to]) { dbase[to][from]=how; }else{ dbase[to]=([from:how]); } } /* 0 means no path. * -1 means already there * otherwise the returned string is a command to take you one * step on the way */ mixed howgetto(string from,string to) { mapping whereto,done; int e,d; string foo; string *alpha,*beta,*gamma,*delta; whereto=([to:""]); done=([]); while(!(foo=whereto[from]) && m_sizeof(whereto)) { done|=whereto; whereto=dbase & whereto; alpha=m_indices(whereto); beta=m_values(whereto); whereto=([]); for(e=0;e<sizeof(alpha);e++) { gamma=m_indices(dbase[alpha[e]]); delta=m_values(dbase[alpha[e]]); foo=beta[e]; for(d=0;d<sizeof(gamma);d++) whereto[gamma[d]]=delta[d]+"\\n"+foo; } whereto-=done; } return foo; } static string current_pos; void goto(string where) { string cmd; if(stringp(cmd=howgetto(current_pos,where))) { send(replace(cmd,"\\n","\n")); write("Msh: Ok.\n"); }else{ write("Msh: don't know the way or no such room.\n"); } } /* Autoactions */ mixed *autoactions=({}); void add_autoaction(string match,mixed action) { autoactions+=({ ({match,sizeof(match/"%"),action}) }); } void remove_autoaction(string a) { autoactions=filter_array(autoactions, lambda(mixed *a,string b) { return a[0]!=b; },a); } void handle_autoactions(string s) { mixed foo; string *a; a=allocate(10); foreach(autoactions,foo) { if(sscanf(s,foo[0]+"%s",a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9])==foo[1]) { if(stringp(foo[2])) { write("Autoaction: doing "+foo[2]+"\n"); s=foo[2]; s=replace(s,a[0..foo[1]-1], ("$1,$2,$2,$4,$5,$6,$7,$8,$9"/",")[0..foo[1]-1]); m_stdin(s); }else if(functionp(foo[2])){ foo[2](a); } } } } void list_autoactions() { mixed foo; foreach(autoactions,foo) { write("\""+foo[0]+"\" -> "); if(stringp(foo[2])) { write(foo[2]); }else if(functionp(foo[2])){ write(function_name(foo[2])+"()"); } write("\n"); } } /* Aliases */ void handle_line(string foo) { handle_autoactions(foo); } static string from_mud_buffer=""; void from_mud(int sock,string foo) { foo-="\r"; write(foo); from_mud_buffer+=foo; while(sscanf(from_mud_buffer,"%s\n%s",foo,from_mud_buffer)) { while(foo[0..1]=="> ") { /* handle_prompt(); */ foo=foo[2..strlen(foo)]; } handle_line(foo); } while(from_mud_buffer[0..1]=="> ") { /* handle_prompt(); */ from_mud_buffer=from_mud_buffer[2..strlen(foo)]; } } string invert_dir(string x) { while(sscanf(x," %s",x)); while(x[-1]==' ') x=x[0..strlen(x)-2]; if(x=movement_verbs[x]) return x; write("Failed to invert direction "+x+"\n"); return "FAILED INVERT"; } static mapping variables=([]); string eval(string s) { mixed a,b; if(!stringp(s)) return s; if(sscanf(s,"%s==%s",a,b)) return eval(a)==eval(b); if(sscanf(s,"%s!=%s",a,b)) return eval(a)!=eval(b); if(sscanf(s,"%s+%s",a,b)) return eval(a)+eval(b); if(sscanf(s,"!%s",a)) return !eval(a); if(sscanf(s,"%*[ ]%d%*[ ]%s",a,b)==4 && b=="") return a; return s; } mixed exec(string b) { return clone_object(make_object("#pragma unpragma_strict_types\n"+ "void create() { update(file_name()); expunge(); }\n"+ "mixed foo() { "+b+" ;}\n"))->foo(); } mapping aliases=([]); string *get_then_else(string s) { int cnt,e,i; string *words; words=s/" "; cnt=1; for(i=e=0;e<sizeof(words);e++) { switch(words[e]) { case "\\if": if(i) { write("error: no then after if.\n"); return 0; } cnt++; i=1; break; case "then": if(!i) { write("error: if without then.\n"); return 0; } i=0; break; case "else": if(i) { write("error: else without then.\n"); return 1; } if(!--cnt) { return ({words[0..e-1]*" ",words[e+1..sizeof(words)]*" "}); } break; } } return s; } void stdin(string bar) { mixed a,b,c,d,*f; int foo; sscanf(bar,"%s\n",bar); if(sscanf(bar,"\\%s",a)) { sscanf(a,"%s %s",a,b); switch(a) { case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": case "10": case "11": case "12": case "13": case "14": case "15": case "16": case "17": case "18": case "19": case "20": sscanf(a,"%d",foo); for(foo++;foo--;) m_stdin(b); break; case "help": write("\\on - list autoaction\n"); write("\\on \"<string>\" <cmd list> - add autoaction\n"); write("\\on <string> - remove autoaction\n"); write("\\at <pos> - tell msh where you are\n"); write("\\from <pos> to <pos> do <cmd list> - Tell msh how to get from pos to pos\n"); write("\\route <pos> to <pos> do <cmd list> - like \\from but adds in both directions\n"); write("\\from <pos> to <pos> - Show how to get from pos to pos\n"); write("\\remove <pos> to <pos> - Remove entry in pos-to-pos database\n"); write("\\save - save to savefile (no automatic saves are done)\n"); write("\\goto <pos> - Send commands to go to pos\n"); write("\\auto <seconds> - Set autologin timeout\n"); write("\\wait <seconds> <cmd list> - Delay a command\n"); write("\\alias - List aliases\n"); write("\\alias <alias> - Remove alias\n"); write("\\alias <alias> <cmd list> - Add an alias\n"); write("\\login - try to login again\n"); break; case "wait": sscanf(b,"%d %s",foo,b); call_out(m_stdin,foo,b); write("msh: Will do "+b+" in "+foo+" seconds.\n"); break; case "login": login(); break; case "alias": if(!b) { sum_arrays(lambda(string foo,string bar) { write(foo+" "[strlen(foo)..1000]+":"+bar+"\n"); },m_indices(aliases),m_values(aliases)); break; } if(sscanf(b,"%s %s",b,c)) { c=replace(c,",","\\n"); aliases[b]=c; write("msh: Ok.\n"); }else{ if(aliases[b]) { aliases=m_delete(aliases,b); write("msh: Ok.\n"); }else{ write("msh: No sush alias.\n"); } } break; case "beep": write(sprintf("%c",7)); break; case "on": if(!b) { list_autoactions(); break; } if(sscanf(b,"\"%s\" %s",a,b)) { b=replace(b,",","\\n"); add_autoaction(a,b); write("msh: ok.\n"); }else{ remove_autoaction(b); write("msh: ok.\n"); } break; case "at": current_pos=b; write("msh: Ok, You're now in: "+current_pos+"\n"); break; case "auto": sscanf(b,"%d",auto_login); write("msh: Autologin set to "+auto_login+" seconds.\n"); break; case "remove": if(!sscanf(b,"%s to %s",a,b)) { write("msh: Usage: \remove <pos> to <pos>\n"); break; } remove_entry(a,b); write("msh: Ok.\n"); break; case "r": case "route": if(!sscanf(b,"%s to %s do %s",a,b,c)) { write("msh: Usage: \route <pos> to <pos> do <list>\n"); break; } c=replace(c,",","\\n"); c=(c/"\\n"-({""}))*"\\n"; f=map_array(c/"\\n",invert_dir); for(foo=0;foo<sizeof(f)/2;foo++) { d=f[foo]; f[foo]=f[sizeof(f)-foo-1]; f[sizeof(f)-foo-1]=d; } d=f*"\\n"; if(sizeof(d/"FAILED INVERT")>1) break; add_entry(a,b,c); add_entry(b,a,d); write("msh: Ok.\n"); break; case "from": case "f": if(!sscanf(b,"%s to %s",a,b)) { write("msh: Usage: \from <pos> to <pos> [do <list>]\n"); break; } if(sscanf(b,"%s do %s",b,c)) { c=replace(c,",","\\n"); if(c[-1]!='\n') c+="\n"; add_entry(a,b,c); write("msh: Ok.\n"); }else{ c=howgetto(a,b); if(!stringp(c)) { write("msh: no known way.\n"); }else{ write("msh: path = "+replace(c,"\\n",",")+"\n"); } } break; case "goto": case "g": goto(b); break; case "clean": autoactions=({}); dbase=([]); write("msh: Ok.\n"); break; case "data": write(code_value(dbase)); write("\n"); break; case "dumpauto": write(code_value(autoactions)); write("\n"); break; case "dump": write("===============================================================\n"); write("Current location: "+current_pos+"\n"); write("===============================================================\n"); break; case ".": from_mud(socket,"> "); break; case "save": save(); write("msh: saved.\n"); break; case "shutdown": shutdown(); break; case "if": if(!sscanf(b,"%s then %s",a,b)) { write("Usage: \\if <stmnt> then <commands> else <commands>\n"); break; } a=eval(a); b=get_then_else(b); if(b) m_stdin(b[!(a && (!stringp(a) || strlen(a)))]); break; case "print": if(!b) { write("Usage: \\print <expression>\n"); break; } a=eval(b); write("Result: "+code_value(a,1)+"\n"); break; case "eval": case "exec": if(a=exec(b)) write("Result: "+code_value(a,1)+"\n"); break; default: if(sscanf(a+(b?" "+b:""),"%s=%s",b,c)) { sscanf(b,"%s ",b); while(sscanf(b," %s",b)); write("Setting $"+b+" to "+c+".\n"); variables[b]=c; break; } write("msh: no such action.\n"); break; case "quote": case "q": if(movement_verbs[b]) current_pos=0; send(b); break; } }else{ c=bar/" "; if(a=aliases[c[0]]) { c[0]=c[1..10000]*" "; b=replace(a, explode("#@,#1,#2,#3,#4,#5,#6,#7,#8,#9",",")[0..sizeof(c)-1], c[0..10]); map_array(b/"\\n",stdin); }else{ if(movement_verbs[bar]) current_pos=0; send(bar); } } } void test1() { write(function_name(this_function())+"\n"); } void test3() { write(function_name(this_function())+"\n"); } void test4() { write(function_name(this_function())+"\n"); } static int login_state; void login_from_mud(int fd,string s) { string tmp; from_mud(fd,s); s=lower_case(s); switch(login_state) { case 0: if(sscanf(s,"%sname%s",tmp,tmp)) { send(name); login_state++; } break; case 1: if(sscanf(s,"%snew%s",tmp,tmp)) login_state++; case 2: case 3: if(sscanf(s,"%spassword%s",s,s)) { socket_write(socket,password+"\n"); if(login_state!=2) set_socket_read(fd,from_mud); login_state++; } } } void connection_closed() { write("Connection closed by foreign host.\n"); if(auto_login) { remove_call_out(login); remove_call_out(login); write("msh: Will try again in "+auto_login+" seconds.\n"); call_out(login,auto_login); } } void login() { int change=0; int err; login_state=0; remove_call_out(login); remove_call_out(login); if(!name) { change++; write("Insert name: "); name=my_gets(); } if(!password) { change++; write("Insert password: "); password=popen("stty -echo ; read 'foo' ; stty echo ; echo $foo"); sscanf(password,"%s\n",password); } if(!address) { change++; write("Insert address: "); address=my_gets(); while(sscanf(address," %s",address)); if(sscanf(address,"%*d.%*d.%*d.%*s")!=4) { string a; int b; if(!sscanf(address,"%s %d",a,b)) { write("Error in address.\n"); exit(1); } write("Looking up address.\n"); a=popen("nslookup "+a); sscanf(a,"%*sName:%*sAddress: %s\n",address); while(sscanf(address," %s",address)); write("Ip number is: "+address+"\n"); address+=" "+b; } } if(change) save(); write("Trying "+address+" ...\n"); socket=socket_create(STREAM | BUFFERED_INPUT | BUFFERED_OUTPUT , test1,connection_closed); socket_connect(socket,address,test3,test4); set_socket_read(socket,login_from_mud); /* check out why this is needed */ /* set_socket_mode(socket,STREAM | BUFFERED_INPUT | BUFFERED_OUTPUT); */ } int main(int argc,string *argv,string *env) { string s; if(argc>1) { file=argv[1]; }else{ write("Save file (press return for no saves) ?: "); file=my_gets(); } if(strlen(file)) if(s=read_bytes(file)) restore_object(s); login(); }