/* Do not remove the headers from this file! see /USAGE for more info. */ /* This will be converted to an inherited part of shells when: * 1) We have programatic security * 2) We have shared variables * (I guess that should hold for all daemons, though...) * Someday might want to have cfile file and dir all accept string*'s in case people want to * use variables for them. Right now, you can only use strings.... */ // cmd_d.c written by Rust (John Viega, rust@virginia.edu) for the Lima mudlib, July 1995. #include <security.h> #define PLURAL 1 #define STR 2 #define NUM 4 #define IS_PATH 8 #define IS_FILE 16 #define IS_DIR 32 #define IS_FNAME 64 #define IS_CFILE 128 #define IS_OBFILE 256 #define FILE (IS_PATH | IS_FILE) #define DIR (IS_PATH | IS_DIR) #define CFILE (IS_PATH | IS_FILE | IS_CFILE) #define OBFILE (IS_PATH | IS_FILE | IS_CFILE | IS_OBFILE) #define FNAME (IS_PATH | IS_FNAME) #define IS_OBJECT 512 #define ONLY_USER 1024 #define OBJECT (IS_OBJECT) #define USER (IS_OBJECT | ONLY_USER) #define FILE_FLAGS (IS_PATH | IS_FILE | IS_DIR | IS_OBFILE | IS_FNAME) inherit M_GLOB; inherit M_REGEX; inherit M_GETOPT; inherit M_ACCESS; private mixed parse_arg(int,string); class proto_info { string options; int * prototype; int first_optional_arg; string proto_string; } private nosave mapping cmd_info = ([]); private nosave mapping proto_info = ([]); void create() { set_privilege(1); } private void parse_verb_defs(string dir, string filecontents) { mapping pathmap = ([]); string* lines = explode(filecontents,"\n"); string* words; string line, cmd, item; int size, flags, error_flag, i; string errors = ""; foreach(line in lines) { words = split(line, "[ \t]+") - ({""," "}); size = sizeof(words); if(!size || words[0][0] == '#') continue; cmd = words[0]; if(!undefinedp(pathmap[cmd])) errors += sprintf("Warning: %s redefined. Previous definition being clobbered.\n", cmd); pathmap[cmd] = new(class proto_info); //### gross. ((class proto_info)pathmap[cmd])->proto_string = replace_string( replace_string( replace_string( replace_string (replace_string (replace_string( implode (words," "), "num","number"), "str","string"), "obj", "object"), "fname", "filename"), "*", "(s)"), "cfile", "file"); if(!(--size)) continue; words = words[1..]; if(words[0][0] == '-' && strlen(words[0]) > 1) { ((class proto_info)pathmap[cmd])->options = words[0][1..]; if(!(--size)) { errors += "Bad line: " + line +"\n"; continue; } words = words[1..]; } ((class proto_info)pathmap[cmd])->prototype = allocate(size); ((class proto_info)pathmap[cmd])->first_optional_arg = -1; for(i=0; i<size;i++) { flags = 0; if(words[i][0] == '[' && words[i][<1] == ']') { words[i] = words[i][1..<2]; if(((class proto_info)pathmap[cmd])->first_optional_arg == -1) ((class proto_info)pathmap[cmd])->first_optional_arg = i; } if(words[i][<1] == '*') { flags |= PLURAL; words[i] = words[i][0..<2]; } if(words[i] == "") { errors += "Bad line: "+line + "\n"; error_flag = 0; break; } foreach(item in explode(words[i],"|")) switch(item) { case "str": flags |= STR; break; case "file": flags |= FILE; break; case "obfile": flags |= OBFILE; break; case "dir": flags |= DIR; break; case "obj": flags |= OBJECT; break; case "user": flags |= USER; break; case "fname": flags |= FNAME; break; case "num": flags |= NUM; break; case "cfile": flags |= CFILE; break; default: errors += "Bad line (invalid argument type): "+line + "\n"; error_flag = 1; break; } if(error_flag) break; ((class proto_info)pathmap[cmd])->prototype[i] = flags; } if(error_flag) { error_flag = 0; continue; } } proto_info[dir] = pathmap; if(strlen(errors)) NEWS_D->system_post(BUG_NEWSGROUP, "Bugs found by " + base_name(this_object()), errors); } private void cache_dir(string dir) { string* files; if(dir[<1] != '/') dir += "/"; if(is_file(dir+"Cmd_rules")) parse_verb_defs(dir, read_file(dir+"Cmd_rules")); files = get_dir(dir + "?*.c"); if(!arrayp(files) || !sizeof(files)) return; cmd_info[dir] = map(files, (: $1[0..<3] :)); } varargs void cache(string* paths) { if(!paths) paths = keys(cmd_info); map(paths,(:cache_dir:)); } int is_command(object o) { mixed ret; if(function_exists("call_main", o) != CMD) return 0; if ((ret = o->not_a_cmd()) && (ret == 1 || ret == file_name(o))) return 0; return 1; } // This one won't match commands not in your path. For players, mainly... varargs mixed find_cmd_in_path(string cmd, string* path) { string dir; object o; foreach(dir in path) { if(dir[<1] != '/') dir += "/"; //Try adding this dir if we don't have it, if(!cmd_info[dir]) cache_dir(dir); //And if it's still not in the cache, this is a bogus path. if(!cmd_info[dir]) continue; if(member_array(cmd, cmd_info[dir]) != -1 && is_file(dir+cmd+".c") && o = load_object(dir+cmd)) { if(!is_command(o)) return -1; return ({ o, dir, cmd }); } } return 0; } varargs mixed find_cmd(string cmd, string* path) { object o; string dir, s; if (member_array('/', cmd) != -1) { s = evaluate_path(cmd); if(o = load_object(s)) { if (is_command(o)) { mixed tmp = split_path(s); dir = tmp[0]; s = tmp[1]; sscanf(s, "%s.c", s); if(!cmd_info[dir]) cache_dir(dir); return ({o, dir, s }); } } } return find_cmd_in_path(cmd, path); } mixed smart_arg_parsing(mixed argv, string* path, string *implode_info) { mixed resv; mixed info; string cmd_name; string this_path; object cmd_obj; class proto_info pstuff; string USAGE; string opstr; mapping ops; int argcounter; int i; mixed expanded_arg; mixed this_arg; int plural; if (sizeof(argv) == 0) return -1; cmd_name = trim_spaces(argv[0]); if (member_array('/', cmd_name) != -1) { array matches = filter_array(glob(cmd_name + ".c"), (: is_file :)); switch (sizeof(matches)) { case 1: if ((cmd_obj = load_object(matches[0])) && is_command(cmd_obj)) { mixed tmp = split_path(matches[0]); this_path = tmp[0]; cmd_name = tmp[1][0..<3]; } else return 0; break; case 0: break; default: printf("Ambiguous expansion for %s.\n", cmd_name); return 1; } } if (!this_path) { info = find_cmd(cmd_name, path); if (intp(info)) return info; cmd_obj = info[0]; this_path = info[1]; cmd_name = info[2]; } if(undefinedp(proto_info[this_path]) || undefinedp(pstuff=proto_info[this_path][cmd_name])) { // no prototypes, so don't do no globbing or nothin'. // In fact, just send back the raw string. if(sizeof(argv) > 1) { // make it so that all non-strings are converted to strings, // since whatever command is going to be expecting a string. argv = map(argv, (: stringp($1) ? $1 : sprintf("%O",$1) :)); return ({cmd_obj, ([]), implode_by_arr(argv[1..], implode_info) }); } else return ({ cmd_obj, ([]), 0}); } // Remove "'s for: "word1 word2" argv = map(argv, (: (stringp($1) && $1[0] == '"' && $1[<1] == '"') ? $1[1..<2] : $1 :)); USAGE = pstuff->proto_string; if(sizeof(argv) > 1) { argv = argv[1..]; if((opstr = pstuff->options)) { info = getopt(argv, opstr); if(!arrayp(info)) return -2; argv = info[1]; ops = info[0]; } } else { argv = ({}); } if(!ops) ops = ([]); argcounter = 0; resv = allocate(sizeof(pstuff->prototype)); for(i=0; i<sizeof(pstuff->prototype); i++) { if(argcounter == sizeof(argv)) { if(i >= pstuff->first_optional_arg && pstuff->first_optional_arg != -1) break; printf("Too few arguments.\nUsage: %s\n", USAGE); return 1; } expanded_arg = parse_arg(pstuff->prototype[i],argv[argcounter++]); if (intp(expanded_arg)) { // error switch (expanded_arg) { case -1: printf("Invalid argument: %O\nUsage: %s\n", argv[argcounter-1], USAGE); break; case -2: printf("Vague argument: %O\nUsage: %s\n", argv[argcounter-1], USAGE); break; case -3: printf("%s: No such file or directory.\n", argv[argcounter-1]); break; } return 1; } plural = pstuff->prototype[i] & PLURAL; this_arg = expanded_arg[0]; resv[i] = expanded_arg[1]; if(!plural) { if(sizeof(resv[i]) > 1) { printf("Vague argument: %s\nUsage: %s\n", argv[argcounter-1], USAGE); return 1; } resv[i] = resv[i][0]; continue; } while (1) { if(argcounter == sizeof(argv)) break; expanded_arg = parse_arg(this_arg, argv[argcounter]); if(!arrayp(expanded_arg)) break; if(sizeof(expanded_arg[1]) == 1 && i+1 != sizeof(pstuff->prototype) && !(pstuff->prototype[i+1]&PLURAL) && (pstuff->prototype[i+1] & expanded_arg[0]) && (argcounter + 1 == sizeof(argv) || intp(parse_arg(pstuff->prototype[i+1], argv[argcounter+1])))) break; resv[i] += expanded_arg[1]; argcounter++; } } if(argcounter != sizeof(argv)) { if(pstuff->prototype[i-1] & PLURAL) printf("%s: not found.\n", argv[argcounter]); else printf("Too many arguments.\nUsage: %s\n",USAGE); return 1; } return ({ cmd_obj, ops, resv }); } private mixed parse_arg(int this_arg, mixed argv) { int hits; mixed untrimmed_argv; array result = ({}); array string_result; untrimmed_argv = argv; if(stringp(argv)) { argv = trim_spaces(argv); if (this_arg & IS_PATH) { string path = evaluate_path(argv); result = glob(path); if((this_arg & IS_CFILE) && ! (this_arg & IS_DIR)) result = filter(result, (:!is_directory($1):)); if (!sizeof(result) && (this_arg & IS_OBFILE)) { object ob = get_object(argv); if (ob) { string bname = base_name(ob); if(is_file(bname + ".c")) result = ({ bname + ".c" }); else { if(is_file(bname + ".scr")) result = ({bname + ".scr"}); } } } if (!sizeof(result) && (this_arg & IS_CFILE)) { int ix = strsrch(path, ".", -1); string extension = path[ix+1..]; if(extension != "c" && extension != "scr") { result = glob(path + ".c") + glob(path + ".scr"); } } if ((this_arg & IS_FILE) && !(this_arg & IS_DIR)) result = filter(result, (: is_file :)); if ((this_arg & IS_DIR) && !(this_arg & IS_FILE)) result = filter(result, (: is_directory :)); if (!sizeof(result) && (this_arg & IS_FNAME)) { if (is_directory(base_path(path))) result = ({ path }); } if (!sizeof(result)) result = 0; else { result = ({ this_arg & FILE_FLAGS, result }); hits++; } } if (this_arg & STR) { string_result = ({ STR, ({ untrimmed_argv }) }); hits++; } } if (this_arg & IS_OBJECT) { object ob; if(stringp(argv)) ob = get_object(argv); else if (objectp(argv)) ob = argv; else ob = 0; if((this_arg & ONLY_USER) && ob && !ob->query_link()) ob = 0; if(ob) { result = ({ this_arg & (IS_OBJECT | ONLY_USER), ({ ob }) }); hits++; } } if (this_arg & NUM) { int tmp; if (intp(argv)) { result = ({ NUM, ({ argv }) }); hits++; } else if (sscanf(argv,"%d",tmp)) { result = ({ NUM, ({ tmp }) }); hits++; } } if(!hits) { if (this_arg & IS_PATH) return -3; else return -1; } if (this_arg & STR) { if (hits == 1) result = string_result; // use string hit else hits--; // discard string hit } if(hits > 1) return -2; return result; }