diff --git a/lib/modules/Tools.pmod/Install.pmod b/lib/modules/Tools.pmod/Install.pmod new file mode 100644 index 0000000000000000000000000000000000000000..2f90c93b094dedbc83e84bc8aeede91b2f86daa7 --- /dev/null +++ b/lib/modules/Tools.pmod/Install.pmod @@ -0,0 +1,196 @@ +// +// Common routines which are useful for various install scripts based on Pike. +// + +string make_absolute_path(string path) +{ +#if constant(getpwnam) + if(sizeof(path) && path[0] == '~') + { + string user, newpath; + sscanf(path, "~%s/%s", user, newpath); + + if(user && sizeof(user)) + { + array a = getpwnam(user); + if(a && sizeof(a) >= 7) + return combine_path(a[5], newpath); + } + + return combine_path(getenv("HOME"), path[2..]); + } +#endif + + if(!sizeof(path) || path[0] != '/') + return combine_path(getcwd(), "../", path); + + return path; +} + +class ProgressBar +{ + private int width = 45; + + private float phase_base, phase_size; + private int max, cur; + private string name; + + void set_current(int _cur) + { + cur = _cur; + } + + void set_name(string _name) + { + name = _name; + } + + void set_phase(float _phase_base, float _phase_size) + { + phase_base = _phase_base; + phase_size = _phase_size; + } + + void update(int increment) + { + cur += increment; + cur = min(cur, max); + + float ratio = phase_base + ((float)cur/(float)max) * phase_size; + if(1.0 < ratio) + ratio = 1.0; + + int bar = (int)(ratio * (float)width); + int is_full = (bar == width); + + // int spinner = (max < 2*width ? '=' : ({ '\\', '|', '/', '-' })[cur&3]); + int spinner = '='; + + write("\r %-13s |%s%c%s%s %4.1f %% ", + name+":", + "="*bar, + is_full ? '|' : spinner, + is_full ? "" : " "*(width-bar-1), + is_full ? "" : "|", + 100.0 * ratio); + } + + void create(string _name, int _cur, int _max, + float|void _phase_base, float|void _phase_size) + /* NOTE: max must be greater than zero. */ + { + name = _name; + max = _max; + cur = _cur; + + phase_base = _phase_base || 0.0; + phase_size = _phase_size || 1.0 - phase_base; + } +} + +class Readline +{ + inherit Stdio.Readline; + + int match_directories_only; + + void trap_signal(int n) + { + werror("\r\nInterrupted, exit.\r\n"); + destruct(this_object()); + exit(1); + } + + void destroy() + { + ::destroy(); + signal(signum("SIGINT")); + } + + static private string low_edit(string data, string|void local_prompt, + array(string)|void attrs) + { + string r = ::edit(data, local_prompt, (attrs || ({})) | ({ "bold" })); + if(!r) + { + // ^D? + werror("\nTerminal closed, exit.\n"); + destruct(this_object()); + exit(0); + } + return r; + } + + string edit(mixed ... args) + { + return low_edit(@args); + } + + string edit_filename(mixed ... args) + { + match_directories_only = 0; + + get_input_controller()->bind("^I", file_completion); + string s = low_edit(@args); + get_input_controller()->unbind("^I"); + + return s; + } + + string edit_directory(mixed ... args) + { + match_directories_only = 1; + + get_input_controller()->bind("^I", file_completion); + string s = low_edit(@args); + get_input_controller()->unbind("^I"); + + return s; + } + + static private string file_completion(string tab) + { + string text = gettext(); + int pos = getcursorpos(); + + array(string) path = make_absolute_path(text[..pos-1])/"/"; + array(string) files = + glob(path[-1]+"*", + get_dir(sizeof(path)>1? path[..sizeof(path)-2]*"/"+"/":".")||({})); + + if(match_directories_only) + files = Array.filter(files, lambda(string f, string p) + { return (file_stat(p+f)||({0,0}))[1]==-2; }, + path[..sizeof(path)-2]*"/"+"/"); + + switch(sizeof(files)) + { + case 0: + get_output_controller()->beep(); + break; + case 1: + insert(files[0][sizeof(path[-1])..], pos); + if((file_stat((path[..sizeof(path)-2]+files)*"/")||({0,0}))[1]==-2) + insert("/", getcursorpos()); + break; + default: + string pre = String.common_prefix(files)[sizeof(path[-1])..]; + if(sizeof(pre)) + { + insert(pre, pos); + } else { + if(!sizeof(path[-1])) + files = Array.filter(files, lambda(string f) + { return !(sizeof(f) && f[0] == '.'); }); + list_completions(sort(files)); + } + break; + } + } + + void create(mixed ... args) + { + signal(signum("SIGINT"), trap_signal); + ::create(@args); + } +}