/* Do not remove the headers from this file! see /USAGE for more info. */ #include <driver/type.h> string evaluate_path(string); object find_body(string); object this_body(); private nosave string array normal_directions = ({ "up", "down", "north", "northeast", "northwest", "east", "southeast", "southwest", "south", "west" }); //FUNCTION is_normal_direction //returns 1 if the direction is 'normal' int is_normal_direction(string dir) { if (member_array(dir, normal_directions) != -1) return 1; return 0; } //:FUNCTION call_trace //returns the stack of objects and functions string call_trace() { string res; int i, n; object array objects; string array programs; string array functions; res = ""; programs = call_stack(0); objects = call_stack(1); functions = call_stack(2); n = sizeof(programs); for (i = 0; i < n; i++) { res += sprintf("%25-O %25-s %25-s\n", objects[i], programs[i], functions[i]); } return res; } // There should be an | operator for this. //:FUNCTION clean_array //returns a version of the passed array with duplicate //entries removed. Eg, clean_array(({1,2,2})) => ({1,2}) mixed* clean_array(mixed* r) { int i, n; r = r & r; // sort. sort_array() can't hack it. And no, &= doesn't work. n = sizeof(r) - 1; while (i < n) { if (r[i] == r[i+1]) { int j = i+1; while (j < n && r[i] == r[j + 1]) j++; r[i..j-1] = ({}); n -= j - i; } i++; } return r; } //:FUNCTION rev_explode //returns a reversable explode, because sometimes this has a desired //effect. This sefun requires SANE_EXPLODE_STRING to work properly //but the mudlib already requires that anyway. string array rev_explode(string arr_in, string delim) { string array arr_out=({}); if(arr_in[0..strlen(delim)-1]==delim) arr_out=({""}); arr_out+=explode(arr_in,delim); if(arr_in[<strlen(delim)..]==delim) arr_out+=({""}); return arr_out; } //:FUNCTION abs // Absolute value function int abs(int x) { return x < 0 ? -x : x; } //:FUNCTION cmp //returns whether its two arguments are equivalent. This is about //the same as using the equivalence operator (==), but will return true //in cases where == does not, such as when comparing 2 arrays. //Logically 2 arrays should be equivalent, but aren't with ==. //cmp knows they are. This is mainly useful when you want to compare //mappings and arrays. int cmp( mixed a, mixed b ) { int i; mixed x, y, z; if( arrayp( a ) && arrayp( b ) ) { if( sizeof(a) != ( i = sizeof(b) ) ) return 0; while( i-- ) if( !cmp(a[i], b[i]) ) return 0; return 1; } if( mapp( a ) && mapp( b ) ) { if( sizeof(a) != (i = sizeof(b)) ) return 0; foreach (x,y in a) { z = b[x]; if (undefinedp(z)) return 0; if (!cmp(y, z)) return 0; } return 1; } return a == b; } //:FUNCTION insert //Inserts the contents of the array of the first argument into //The array in the second argument before the nth element of the array, //where n is the 3rd argument passed to insert. // Rust hacked at this to make it a bit more intuitive... mixed insert( mixed to_insert, mixed into_array, int where ) { //### (db) I don't see any particular need to program defensively... #if 0 if( !arrayp( to_insert ) ) return (void)error("Bad type arg 1 to simul efun insert()"); if( !arrayp( into_array ) ) return (void)error("Bad type arg 2 to simul efun insert()"); if( !intp( where ) ) return (void)error("Bad type arg 3 to simul efun insert()"); #endif return into_array[0..where-1] + to_insert + into_array[where..]; } string parse_ref(string Ref) { // If the first thing is a period, we can ignore it. string s = Ref[0] == '.' ? Ref[1..] : Ref; string result; string noun; string* pieces = explode(s,":"); noun = pieces[0]; pieces = pieces[1..]; switch ( noun ) { case "me": result = "this_body()"; break; case "here": result = "environment(this_body())"; break; default: if ( find_body(noun) ) result = "find_body(\""+noun+"\")"; else if ( load_object(evaluate_path(noun)) ) result = "find_object(evaluate_path(\""+noun+"\"))"; else return Ref; } foreach ( noun in pieces ) { switch ( noun ) { case "shell": result = sprintf("(%s->query_shell_ob() || %s)", result, result); break; case "link": result = sprintf("(%s->query_link())",result); break; default: result = sprintf("present(\"%s\",%s)", noun, result); break; } } return result; } object parse_ref_into_obj(string Ref) { string s = Ref[0] == '.' ? Ref[1..] : Ref; object result; string noun; string *pieces = explode(s,":"); noun = pieces[0]; pieces = pieces[1..]; switch ( noun ) { case "me": result = this_body(); break; case "here": result = environment(this_body()); break; default: if ( find_body(noun) ) result = find_body(noun); else if ( !(result = load_object(evaluate_path(noun))) ) return 0; } foreach ( noun in pieces ) { switch ( noun ) { case "shell": result = result->query_shell_ob() || result; break; case "link": result = result->query_link(); break; default: result = present(noun, result); break; } } return result; } /* eval lets you evaluate a string as an expression. exec_code allows you to evaluate a string as LPC code. */ varargs mixed exec_code(string arg, string dir, string includefile) { // DO NOT USE UNGUARDED IN HERE // If perms are bad, we want to fail. Use unguarded(1, (: exec_code :)) // if really necessary -Beek string file = dir + "/exec.c"; object tmp; mixed retval; mixed info; int i; string contents; if(tmp = find_object(file)) destruct(tmp); rm(file); if(!stringp(arg)) error("Bad type argument 1 to exec_code"); info = reg_assoc(arg, ({"\\.[a-zA-Z:/\_]+"}),({0}))[0]; for(i=0; i < sizeof(info);i++) { if(info[i][0] == '.' && strlen(info[i]) > 1 ) info[i] = parse_ref(info[i]); } arg = implode(info,""); if (strsrch(arg,";")==-1) arg = "return "+arg; contents = @END #include <mudlib.h> #include <security.h> inherit M_ACCESS; create() { set_privilege(1); } END; if(includefile) contents += sprintf("\n#include \"%s\"\n", includefile); contents += sprintf( "mixed exec_foo(){ %s", arg ); contents += ";}\n"; write_file(file, contents); retval = file->exec_foo(); rm(file); return retval; } // eval by Rust, so that you can // convert a string to almost anything. // bet on it being as slow as dirt, though... //:FUNCTION eval //evaluates the string s as an LPC value. Eg, if you have a string //someonetyped in: "({1,2,3,4})" eval returns the actual array //({1,2,3,4}). varargs mixed eval( string arg, string includefile ) { mixed tmp; string file; file = "/tmp/eval.c"; if( tmp = find_object( file ) ) destruct( tmp ); rm(file); if( !stringp( arg ) ) { error( "Bad type argument 1 to eval" ); return; } if(includefile) write_file(file,sprintf("#include \"%s\"\n",includefile)); if( arg == "0" ) return 0; if( tmp = to_int( arg ) ) if(tmp + "" == arg) return tmp; if( tmp = to_float( arg ) ) if(tmp + "" == arg) return tmp; if( tmp = find_object(arg) && objectp( tmp ) ) return tmp; if( strlen( arg ) < 4 ) return arg; write_file( file, sprintf( "mixed foo(){ return %s; }\n", arg ) ); if( catch( tmp = file->foo() ) ) return arg; if( tmp && !stringp(tmp) ) return tmp; return arg; } //:FUNCTION decompose //Takes any arrays that are elements in arr and merges //all of its elements as elements of arr. Eg, decompose(({1,({2,3,}),4})) //will return: ({1,2,3,4}). The algorithm is not recursive, so if any of //the arrays have arrays in them, those arrays remain intact. Eg, //decompose( ({1,({({2,3}),4}),5}) ) returns:({1,({2,3}),4,5}). //See flatten_array for a recursive version. mixed* decompose( mixed* org ) { int i = 0, j; if( !arrayp( org ) ) error("Bad type arg to decompose"); org = copy(org); while (i < sizeof(org)) { if (arrayp(org[i])) { j = sizeof(org[i]); org[i..i] = org[i]; i += j; // skip the elements inserted } else i++; } return org; } //:FUNCTION choice //Returns a random element of the structure passed, if that // is an aggregate type (i.e., A string, array or mapping). mixed choice( mixed f ){ mixed *k; switch(typeof(f)){ case STRING: return f[random(strlen(f))]; case ARRAY: return f[random(sizeof(f))]; case MAPPING: k = keys(f); return f[k[random(sizeof(k))]]; default: error("choice of non-sequential type"); } } //:FUNCTION min //Returns the smallest element of an aggregate type (string, array, //or mapping). mixed min( mixed f ){ if(stringp(f)) f = explode(f," "); else if(mapp(f)) f = keys(f); return sort_array(f,1)[0]; } //:FUNCTION max //Returns the largest element of a structure that is a string, //array or mapping. mixed max( mixed f ){ if(stringp(f)) f = explode(f," "); else if(mapp(f)) f = keys(f); return sort_array(f,-1)[0]; } //:FUNCTION flatten_array //Takes a array that may contain arrays, and reduces all //arrays so that the result is a one dimensional array mixed flatten_array(mixed arr) { int i = 0; if (!arrayp(arr)) error("Bad argument 1 to flatten_array"); arr = copy(arr); while (i < sizeof(arr)) { if (arrayp(arr[i])) { arr[i..i] = arr[i]; } else i++; } return arr; } //:FUNCTION call_out_chain //Does a call_out to a list of functions, one following //another, with each returning the delay till the next one is called. protected void handle_chain(object ob, array funcs, array args) { int delay; if(!sizeof(funcs)) return; if (stringp(funcs[0])) { delay = call_other(ob, funcs[0], args...); } else { delay = evaluate(funcs[0], args...); } call_out( (: handle_chain :), delay, ob, funcs[1..], args); } void call_out_chain(array funcs, int delay, array args...) { call_out( (: handle_chain :), delay, previous_object(), funcs, args); } mixed *my_call_outs() { object p = previous_object(); return filter_array(call_out_info(), (: $1[0] == $(p):)); } // Beek fixed not to loop forever if exp < 0, etc // also optimized extensively int pow(int num, int exp) { int res; switch (exp) { case 0: if (!num) error("pow(0, 0) is undefined.\n"); return 1; case 1: return num; case 2: return num * num; case 3: return num * num * num; case 4: // 2 multiplications, not 3 num *= num; // 1, 2, 4 return num * num; case 5: // 3 multiplications, not 4 exp = num * num; // 1, 2, 4, 5 exp *= exp; return exp * num; case 6: // 3 multiplications, not 5 num *= num; // 1, 2, 4, 6 return num * num * num; case 7: // 4 multiplications, not 6 exp = num * num; // 1, 2, 4, 6, 7 exp *= exp * exp; return exp * num; case 8: // 3 multiplications, not 7 num *= num; // 1, 2, 4, 8 num *= num; return num * num; case 9: // 4, not 8 exp = num * num; exp *= exp; exp *= exp; return num * exp; case 10: // 4, not 9 num *= num; exp = num * num; exp *= exp; return exp * num; case 11: // 5, not 10 exp = num * num; res = exp * exp; res *= res; return res * num * exp; case 12: // 4, not 11 ... num *= num; num *= num; exp = num * num; return exp * num; default: if (exp < 0) return 0; // exp > 12 here ... switch (num) { case 0: return 0; case 1: return 1; case 2: return 1 << exp; default: // sub optimal, but pretty good // O(log n) multiplications, not O(n) res = 1; while (exp) { if (exp & 1) res *= num; num *= num; exp >>= 1; } return res; } } } int fuzzy_divide(int top, int bottom) { if (random(bottom) < (top % bottom)) return top / bottom + 1; else return top / bottom; } string implode_by_arr(string array arr1, string array arr2) { string res = ""; int i; if(sizeof(arr2) != (sizeof(arr1) + 1)) error("second arg needs to have 1 more arg than first.\n"); res += arr2[0]; for(i=0;i<sizeof(arr1);i++) { res += arr1[i]; res += arr2[i+1]; } return res; } /* This one by cowl. */ /* Cleaned up, optimized, and extended by Beek */ /* * type 0 = 3 hours 5 minutes 32 seconds * type 1 = 3h5m32s * type 2 = 3h * type 3 = 3h 5m 32s */ string convert_time(int sec, int type) { int weeks, days, hours, minutes, seconds; string ret; minutes=sec/60; seconds=sec-(minutes*60); hours=minutes/60; minutes=minutes-(hours*60); days=hours/24; hours=hours-(days*24); weeks=days/7; days=days-(weeks*7); ret = ""; switch (type) { case 0: if(weeks) ret += M_GRAMMAR->number_of(weeks, "week") + " "; if(days) ret += M_GRAMMAR->number_of(days, "day") + " "; if(hours) ret += M_GRAMMAR->number_of(hours, "hour") + " "; if(minutes) ret += M_GRAMMAR->number_of(minutes, "minute") + " "; if (seconds && ret != "") ret += "and "; if (seconds) ret += M_GRAMMAR->number_of(seconds, "second") + " "; ret = ret[0..<2]; break; case 1: if (weeks) ret += weeks+"w"; if (days) ret += days+"d"; if (hours) ret += hours+"h"; if (minutes) ret += minutes+"m"; if (seconds) ret += seconds+"s"; break; case 2: if (weeks) { ret += weeks+"w"; break; } if (days) { ret += days+"d"; break; } if (hours) { ret += hours+"h"; break; } if (minutes) { ret += minutes+"m"; break; } if (seconds) { ret += seconds+"s"; break; } break; case 3: if (weeks) ret += weeks+"w "; if (days) ret += days+"d "; if (hours) ret += hours+"h "; if (minutes) ret += minutes+"m "; if (seconds) ret += seconds+"s "; ret = ret[0..<2]; break; } return ret; } //:FUNCTION sort_by_value //sort_by_value(arr, f) returns the array arr sorted in such //a way that the elements are in increasing order, as defined by the //value of the function f array sort_by_value(array arr, function value_func) { return sort_array(arr, (: evaluate($(value_func), $1) - evaluate($(value_func), $2) :)); } /* Replacement for the dump_socket_status() efun */ string dump_socket_status() { string ret = "Fd State Mode Local Address Remote Address\n" "-- --------- -------- --------------------- ---------------------\n"; foreach (array item in socket_status()) { ret += sprintf("%2d %|9s %|8s %-21s %-21s\n", item[0], item[1], item [2], item[3], item[4]); } return ret; } string lima_version() { return "Lima 1.0b5"; }