/***************************************************************************/ /* more file sept. 93 Zilanthius, revised|enhanced from player */ /* some people may ask why I used read_bytes() instead of using read_file() * in some cases. Well the reason is quite simple, the MSDOS port read_file() * which I use for developed crashes every time it thinks i over use it. * AAAARGH!! * * Zil */ /* update: Nov 93 * clone this, and move it to player before calling more(file) * each cloned object will more only one file. It destructs itself * when quitting out of the more. * * it allows a player to open multiple more objects and switch * between them. */ #include <mudlib.h> #include <move.h> #include <bad_file.h> #include <ansi.h> #define MAX_BYTE_READ 8192 /* read file in 8KB blocks */ #define THIS_WIZARD this_player()->query_security_level() #define PASTE "/"+ PASTE_DIR +"/"+(string)this_player()->query_name(1) static string more_filename; /* file currently mored */ static string old_search; /* last search string, so "/" only works */ static int total_lines; /* total lines of file (Is this needed?) */ static int current_line; /* line to cat(file) */ static status more_prompt_off; /* search & replace */ string replace_file; string replace; string search; int last_byte; int num_replaced; int num_occured; /* link list */ static object prev_more; /* double linked more objects */ static object next_more; /* when we in another more */ static int current_number; /* current active more object */ static int total_links; /* total more objects */ static int link_number; /* link number */ static int MORE_BLOCK; /**************************************************************************/ /* fn prototypes */ status more(string str); void more_file(); void more_prompt(); static void answer_prompt(string str); static void search_string(string str); static void save_mored_file_with_overwrite(string str); static void save_mored_file(string str); static void dump_mored_file(string str); static void copy_file(string in, string out, status overwrite); void set_prev_more(object arg); void set_next_more(object arg); object get_top_more(); object get_bottom_more(); object get_more_ob(int i); varargs void change_more(int i, status new); void current_more(int i, int j); void max_more(int j); void add_more(object arg); static void quit(); void Quit(); void list_files(); void show_file(); void replace_file(string str); void replace_search(string str); void replace_prompt(); void answer_replace_prompt(string ans); void replace(status all); /**************************************************************************/ /* object id */ status id(string str) { return str == "more"; } status drop() { quit(); return 1; } /**************************************************************************** /* start more here */ status more(string str) { int f_size, byte_size, current_byte; int i; string txt, file; int lines_in_last_block; object ob; if(more_filename || environment() != this_player()) { quit(); return 1; } if(!str) { str = "/"+file_name(environment(this_player()))+".c"; } /* has wizard|player got read access */ if(!(file = (string)this_player()->valid_read(str))) { write("Invalid access: "+str+"\n"); quit(); return 1; } file = "/" + file; /* does file exist */ if(bad_file(file)) { write("Illegal character in filename.\n"); quit(); return 1; } if((f_size = file_size(file)) < 0) { if(this_player()->query_security_level()) { write("Non-existing file: "+ file +"\n"); } else { write("No Such File.\n"); } quit(); return 1; } if(!f_size) { write("Empty file: "+ file +"\n"); quit(); return 1; } /* reset global variables */ more_filename = file; current_line = 1; total_lines = 0; old_search = ""; /* find the files total line length */ while(current_byte < f_size) { if(current_byte + MAX_BYTE_READ >= f_size) { byte_size = f_size - current_byte; } else { byte_size = MAX_BYTE_READ; } txt = read_bytes(file, current_byte, byte_size); if(txt) { #ifdef OLD_EXPLODE lines_in_last_block = sizeof(explode(txt + "\n","\n")) - 1; #else lines_in_last_block = sizeof(explode(txt,"\n")) - 1; #endif /* OLD_EXPLODE */ total_lines += lines_in_last_block; } current_byte += byte_size; #ifdef MSDOS /* hack to account for CR-LF in Olav's msdos compilation */ current_byte += lines_in_last_block; #endif } MORE_BLOCK = 18; ob = present("more 2", this_player()); if(!ob || ob == this_object()) ob = present("more",this_player()); ob->add_more(this_object()); return 1; } void more_file() { if(current_number != link_number) { change_more(current_number); return; } if(current_line < 1) current_line = 1; if(!cat(more_filename,current_line, MORE_BLOCK)) { write(" -=- EOF -=-\n"); quit(); return; } more_prompt(); return; } /* make the more prompt */ void more_prompt() { int percent, f_size; string str; if(current_number != link_number) { change_more(current_number); return; } if(!more_filename) { quit(); return; } if(current_line + MORE_BLOCK > total_lines) { percent = 100; } else { percent = ((current_line + MORE_BLOCK)*100)/total_lines; } f_size = file_size(more_filename); #ifdef MSDOS /* make file_size look the same as in ed (-CR = 1 byte) */ f_size -= total_lines; #endif if(THIS_WIZARD) { str = "More("+ link_number +"/"+ total_links +"): "+ more_filename+" ("+f_size+" bytes), "; } else { str = "More("+ link_number +"/"+ total_links +"): "; } if(strlen(str) > 45) str += "\n "; if(!more_prompt_off) write("\n"+ str+percent+"% [<cr>,u,b,f,-/+,/,q,!,?] "); input_to("answer_prompt"); return; } static void answer_prompt(string str) { int i; string tmp; if(!str || str == "" || str[0] == 'n') { current_line += MORE_BLOCK; } else if(sscanf(str, "-%d", i)) { current_line -= i; } else if(str[0] == '-') { current_line -= 1; } else if(sscanf(str, "+%d", i)) { current_line += i; } else if(str[0] == '+') { current_line += 1; } else if(sscanf(str, "%d", i)) { current_line = i; } else if(str[0] == 'u' || str[0] == 'p') { current_line -= MORE_BLOCK; } else if(str[0] == 'f') { current_line += 5; } else if(str[0] == 'b') { current_line -= 5; } else if(sscanf(str,"/%s",tmp)) { search_string(tmp); return; } else if(str[0] == '/') { search_string(""); return; } else if(str[0] == 'h' || str[0] == '?') { write(" -=[ More Help ]=-\n\n"); write(" Command Notes\n"); write(" u,p One page up.\n"); write(" <cr>,n One page down.\n"); write(" b,f Up/Down 5 lines.\n"); write(" # Goto line #.\n"); write(" +/-# Jump # lines up/down.\n"); write(" ! !external command.\n"); if(THIS_WIZARD) { write(" s,S Save "+more_filename+", S is with overwrite.\n"); write(" d Dump page to file, if exists, appends to file.\n"); write(" r Search & Replace in new file.\n"); write(" >file Open another 'mored' file.\n"); write(" o Toggle prompt line off|on, useful for screen captures.\n"); } write(" ># Change to more number #.\n"); write(" l Number of lines in file.\n"); write(" q,x,Q,X quit more; Quit all mored files.\n"); write(" /str Search for string \"str\"\n"); write(" c clip page to clipboard\n"); write(" m# Screen Mode, # lines (Default: 18 lines)\n"); write(" other Rewrite page.\n"); more_prompt(); return; } else if(str[0] == 'q' || str[0] == 'x') { quit(); return; /* quit more */ } else if(str[0] == 'l') { int l; l = current_line + MORE_BLOCK; l = (l > total_lines) ? total_lines : l; write("Line: ("+current_line+"-"+l+")/"+total_lines+"\n"); more_prompt(); return; } else if(str[0] == 'o') { more_prompt_off = !more_prompt_off; write("Prompt line "+((more_prompt_off) ? "Off.\n" : "On.\n")); more_prompt(); return; } else if(str[0] == '!') { command(extract(str,1), this_player()); more_prompt(); return; } else if(str[0] == 'm') { sscanf(str,"m%d",i); if(i < 1 || i > 45) i = 18; write("Screen Mode set to "+i+" lines.\n"); MORE_BLOCK = i; more_prompt(); return; } else if(sscanf(str,">%d",i)) { if(i > 0 && i <= total_links) { current_number = i; } else { write("More number "+i+" is not open.\n"); more_prompt(); return; } } else if(str[0] == 'Q' || str[0] == 'X') { get_bottom_more()->Quit(); return; } else if(str[0] == 'c') { dump_mored_file(PASTE); return; } else if(THIS_WIZARD) { if(str[0] == 'r') { write("Enter Filename to write Replacement file.\n"+ "(Default Dir: /"+(string)this_player()->query_path()+"): "); input_to("replace_file"); return; } if(str[0] == 's') { write("Enter Save Filename (Default Dir: /"+ (string)this_player()->query_path()+"): "); input_to("save_mored_file"); return; } else if(str[0] == 'S') { write("Enter Save Filename (Default Dir: /"+ (string)this_player()->query_path()+"): "); input_to("save_mored_file_with_overwrite"); return; } else if(str[0] == 'd') { write("Enter Dump Filename (Default Dir: /"+ (string)this_player()->query_path()+"): "); input_to("dump_mored_file"); return; } else if(str[0] == '>') { object ob; if(str == ">") { list_files(); more_prompt(); return; } else { sscanf(file_name(this_object()),"%s#%d", tmp, i); ob = clone_object(tmp); /* generic self clone */ #ifdef NATIVE_MODE ob->move(environment()); #else move_object(ob, environment()); #endif /* NATIVE_MODE */ ob->more(extract(str,1)); if(!ob) more_prompt(); /* new more object may destruct itself */ return; } } } /* else rewrite page */ more_file(); return; } static void search_string(string str) { int i; int lines_in_block; /* number of lines in a file block */ int lines_before; /* number of lines before a file block */ int lines; /* number of lines searched in a block */ int abort; /* number of blocks read, if > 40 abort */ string txt; /* text of a block */ string *txt_list; /* list separating search string */ int current_byte; /* file handle */ int max_byte; /* max bytes read from file */ int f_size; /* file size */ /* is this a repeat of an old search? */ if(!str || str == "") { if(old_search == "") { more_prompt(); return; } str = old_search; } else { old_search = str; } /* read through file */ f_size = file_size(more_filename); while(current_byte < f_size) { if(abort++ > 30) break; /* aborts if file > 240 kB! tested to 140kB */ if(current_byte + MAX_BYTE_READ > f_size) max_byte = f_size - current_byte; else max_byte = MAX_BYTE_READ; txt = read_bytes(more_filename, current_byte, max_byte); #ifdef OLD_EXPLODE lines_in_block = sizeof(explode(txt + "\n","\n")) - 1; #else lines_in_block = sizeof(explode(txt, "\n")) - 1; #endif if(lines_in_block + lines_before < current_line) { lines_before += lines_in_block; current_byte += max_byte; #ifdef MSDOS /* add 1 byte for each CR */ current_byte += lines_in_block; #endif /* MSDOS */ continue; } #ifdef OLD_EXPLODE txt_list = explode(txt + str, str); #else txt_list = explode(txt, str); #endif /* OLD_EXPLODE */ for(i = 0; i < sizeof(txt_list); i++) { if(i) { string tmp; tmp = implode(txt_list[0..i], str); #ifdef OLD_EXPLODE lines = sizeof(explode(tmp + "\n", "\n")); #else lines = sizeof(explode(tmp, "\n")); #endif /* OLD_EXPLODE */ } else { #ifdef OLD_EXPLODE lines = sizeof(explode(txt_list[0] + "\n", "\n")); #else lines = sizeof(explode(txt_list[0], "\n")); #endif /* OLD_EXPLODE */ } if(lines_before + lines > current_line && i < sizeof(txt_list) - 1) { current_line = lines_before + lines; if(this_player()->ansi_on() && (txt = read_file(more_filename,current_line,MORE_BLOCK))) { #ifdef OLD_EXPLODE txt_list = explode(txt + str, str); #else txt_list = explode(txt, str); #endif /* OLD_EXPLODE */ txt = implode(txt_list, BOLD + str + OFF); write(txt); more_prompt(); return; } more_file(); return; } } lines_before += lines_in_block; current_byte += max_byte; #ifdef MSDOS /* add 1 byte for each CR */ current_byte += lines_in_block; #endif /* MSDOS */ } /* can't find string or search aborted */ if(abort > 30) { write("Evaluation too long, aborting search.\n"); } else { write("Cannot match "+str+" after line "+current_line+".\n"); } more_prompt(); return; } /* save Mored file */ static void save_mored_file_with_overwrite(string str) { if(!bad_file(str)) { copy_file(more_filename, str, 1); } more_prompt(); } static void save_mored_file(string str) { if(!bad_file(str)) { copy_file(more_filename, str, 0); } more_prompt(); } /* dump page to file */ static void dump_mored_file(string str) { string txt; if(bad_file(str)) { more_prompt(); return; } if(!(str = (string)this_player()->valid_write(str))) { write("Invalid File Access.\n"); more_prompt(); return; } str = "/" + str; if(file_size(str) >= 0) { if(str == PASTE) rm(PASTE); else write("Appending to File: "+str+"\n"); } write("Dumping screen to "+ ((str == PASTE) ? "clipboard." : str) +"\n"); txt = read_file(more_filename, current_line, MORE_BLOCK); write_file(str, txt); more_prompt(); } /************************************************************************/ /* copy a file from in to out */ static void copy_file(string in, string out, status overwrite) { int f_size, current_byte; int max_byte; string txt; if(!(in || out) || in == "" || out == "") return; if(!(in = (string)this_player()->valid_read(in))) { write("Invalid Read Access.\n"); return; } in = "/"+in; if(!(out = (string)this_player()->valid_write(out))) { write("Invalid Write Access.\n"); return; } out = "/"+out; if(file_size(in) < 0) { write("File: "+in+" does not exist.\n"); return; } if(file_size(out) >= 0) { if(!overwrite) { write("File Already Exists: "+out+"\n"); return; } if(out == in) { write("Cannot copy file onto itself.\n"); return; } write("Removing old file....\n"); rm(out); } write("Copying "+in+" to "+out+"\n"); f_size = file_size(in); while(current_byte < f_size) { if(current_byte + MAX_BYTE_READ > f_size) max_byte = f_size - current_byte; else max_byte = MAX_BYTE_READ; txt = read_bytes(in, current_byte, max_byte); write_file(out, txt); current_byte += max_byte; #ifdef MSDOS /* add in the extra byte for CR when in dos */ #ifdef OLD_EXPLODE current_byte += sizeof(explode(txt + "\n", "\n")) - 1; #else current_byte += sizeof(explode(txt, "\n")) - 1; #endif #endif /* MSDOS */ } } /************************************************************************/ /* list of mored files */ void list_files() { object head; write("File(s): \n"); head = get_bottom_more(); head->show_file(); } void show_file() { write(link_number+". "+more_filename+"\n"); if(next_more) next_more->show_file(); } /************************************************************************/ /* set links */ void set_prev_more(object arg) { prev_more = arg; } void set_next_more(object arg) { next_more = arg; } /************************************************************************/ /* find link limits */ object get_top_more() { /* get link head */ if(next_more) return (object)next_more->get_top_more(); return this_object(); } object get_bottom_more() { /* get link base */ if(prev_more) return (object)prev_more->get_bottom_more(); return this_object(); } object get_more_ob(int i) { if(i == link_number) return this_object(); if(i > link_number && next_more) return (object)next_more->get_more_ob(i); if(i < link_number && prev_more) return (object)prev_more->get_more_ob(i); return 0; /* this should not happen */ } /**********************************************************************/ /* number links */ varargs void change_more(int i, status new) { object head; head = get_bottom_more(); head->current_more(i,0); if(i > total_links) { return (void)head->change_more(total_links, new); /* Error */ } head = (object)head->get_more_ob(i); if(head) { if(new) head->more_file(); else head->more_prompt(); } } void current_more(int i, int j) { current_number = i; j += 1; link_number = j; if(next_more) next_more->current_more(i,j); else max_more(j); } void max_more(int j) { total_links = j; if(prev_more) prev_more->max_more(j); } /**********************************************************************/ /* add link */ void add_more(object new) { object head; head = get_top_more(); if(head != new) { /* for more(1): head == new == this_object() */ head->set_next_more(new); new->set_prev_more(head); } head = (object)new->get_bottom_more(); head->change_more(total_links+1,1); } /*************************************************************************/ /* quit */ static void quit() { object ob; if(next_more) { next_more->set_prev_more(prev_more); ob = (object)next_more->get_top_more(); } if(prev_more) { prev_more->set_next_more(next_more); ob = (object)prev_more->get_top_more(); } if(ob && ob != this_object()) { ob->change_more(1); /* 1 must exist */ } destruct(this_object()); } /************************************************************************/ /* Quit */ void Quit() { /* destruct all list - call from bottom of list */ if(next_more) next_more->Quit(); destruct(this_object()); } /*************************************************************************/ /* search & replace */ void replace_file(string str) { replace_file = (string)this_player()->valid_write(str); if(!bad_file(str)) { replace_file = "/" + replace_file; if(file_size(replace_file) > 0) { write("Cannot overwrite an existing file.\n"); more_prompt(); return; } write(" Search Pattern: "); input_to("replace_search"); return; } more_prompt(); } void replace_search(string str) { if(!str || strlen(str) < 2) { write("Invalid search string.\n"); more_prompt(); return; } search = str; write("Replace Pattern: "); input_to("replace_pattern"); } void replace_pattern(string str) { if(!str) str = ""; replace = str; last_byte = 0; replace(0); } void replace_prompt() { if(this_player()->ansi_on()) { write("Replace \""+ BOLD + search + OFF +"\" with \""+ replace+"\" (y/n/r/q) [n]: "); } else { write("Replace \""+ search +"\" with \""+ replace+"\" (y/n/r/q) [n]: "); } input_to("answer_replace_prompt"); } static void answer_replace_prompt(string ans) { if(ans == "q") { write("Quitting: File "+replace_file+" not complete.\n"); more_prompt(); return; } if(ans == "r") { write("Replacing all occurrances...\n"); write_file(replace_file, replace); replace(1); return; } if(ans == "y") { write("Replacing...\n"); write_file(replace_file, replace); ++num_replaced; } else { write("Skipping...\n"); write_file(replace_file, search); } replace(0); } static void replace(status all) { int max_byte; /* max bytes read from file */ int f_size; /* file size */ string txt1, txt2, tmp; int abort; f_size = file_size(more_filename); while(last_byte < f_size) { txt2 = 0; if(last_byte + MAX_BYTE_READ > f_size) max_byte = f_size - last_byte; else max_byte = MAX_BYTE_READ; txt1 = read_bytes(more_filename, last_byte, max_byte); if(txt1) { sscanf(txt1,"%s"+ search +"%s", txt1, txt2); write_file(replace_file, txt1); last_byte += strlen(txt1); if(txt2) last_byte += strlen(search); #ifdef MSDOS /* add 1 byte for each CR */ #ifdef OLD_EXPLODE last_byte += sizeof(explode(txt1 + "\n","\n")) - 1; #else last_byte += sizeof(explode(txt1,"\n")) - 1; #endif #endif /* MSDOS */ if(txt2) { ++num_occured; if(all && ++abort <= 30) { write_file(replace_file, replace); ++num_replaced; continue; } if(strlen(txt1) > 300) { txt1 = extract(txt1, strlen(txt1)-300); } sscanf(txt1,"%s\n%s", tmp, txt1); write(txt1); if(this_player()->ansi_on()) { write(BOLD+search+OFF); } else { write("^*^"+ search +"^*^"); } if(strlen(txt2) > 200) { txt2 = extract(txt2, 0, 200) + "\n"; } write(txt2 +"\n"); if(abort > 30) { write("Replaced "+num_replaced+" occurrances. Continue?\n"); } replace_prompt(); return; } } } if(this_player()->ansi_on()) { write("Found "+ num_occured +" occurances of \""+ BOLD + search + OFF + "\" in "+more_filename+"\n"+ "Replaced "+ num_replaced +" of them with \""+ replace + "\" in "+replace_file+"\n"); } else { write("Found "+ num_occured +" occurances of \""+ search + "\" in "+more_filename+"\n"+ "Replaced "+ num_replaced +" of them with \""+ replace + "\" in "+replace_file+"\n"); } more_prompt(); }