81a6491999-10-21Marcus Comstedt // $Id: Readline.pike,v 1.28 1999/10/21 14:37:15 marcus Exp $
f9322c1999-03-13Marcus Comstedt  class OutputController { static private object outfd, term; static private int xpos = 0, columns = 0;
ff62d71999-03-23Marcus Comstedt  static private mapping oldattrs = 0;
69d8bc1999-06-22Marcus Comstedt #define BLINK 1 #define BOLD 2 #define DIM 4 #define REVERSE 8 #define ITALIC 16 #define STANDOUT 32 #define UNDERLINE 64 static private int selected_attributes = 0, needed_attributes = 0; static private int active_attributes = 0; static int low_attribute_mask(array(string) atts) { return `|(@rows(([ "blink":BLINK, "bold":BOLD, "dim":DIM, "reverse":REVERSE, "italic":ITALIC, "standout":STANDOUT, "underline":UNDERLINE ]), atts)); } static void low_set_attributes(int mask, int val, int|void temp) { int i, remv = mask & selected_attributes & ~val; string s = ""; if(remv & 15) { s += term->put("me")||""; needed_attributes |= selected_attributes; active_attributes = 0; } if(temp) { needed_attributes |= remv; } else { selected_attributes &= ~remv; needed_attributes &= ~remv; } active_attributes &= ~remv; for(i=0; remv; i++) if(remv & (1<<i)) { string cap = ({0,0,0,0,"ZR","se","ue"})[i]; if(cap && (cap = term->put(cap))) s += cap; remv &= ~(1<<i); } if(sizeof(s)) outfd->write(s); int add = mask & val & ~selected_attributes; selected_attributes |= add; needed_attributes |= add; } static void low_disable_attributes() { low_set_attributes(active_attributes, 0, 1); } static void low_enable_attributes() { int i, add = needed_attributes; string s = ""; needed_attributes &= ~add; for(i=0; add; i++) if(add & (1<<i)) { string cap = ({"mb","md","mh","mr","ZH","so","us"})[i]; if(cap && (cap = term->put(cap))) { s += cap; active_attributes |= i; } add &= ~(1<<i); } if(sizeof(s)) outfd->write(s); } void turn_on(string ... atts) { low_set_attributes(low_attribute_mask(atts), ~0); } void turn_off(string ... atts) { low_set_attributes(low_attribute_mask(atts), 0); }
ff62d71999-03-23Marcus Comstedt  void disable() { catch{ if(oldattrs) outfd->tcsetattr((["OPOST":0,"ONLCR":0,"OCRNL":0, "OLCUC":0,"OFILL":0,"OFDEL":0, "ONLRET":0,"ONOCR":0])&oldattrs); else outfd->tcsetattr((["OPOST":1])); }; } void enable() { if(term->put("cr") && term->put("do")) catch { outfd->tcsetattr((["OPOST":0])); }; else catch { outfd->tcsetattr((["OPOST":1,"ONLCR":1,"OCRNL":0,"OLCUC":0, "OFILL":1,"OFDEL":0,"ONLRET":0,"ONOCR":0]));}; } void destroy() { disable(); }
f9322c1999-03-13Marcus Comstedt  void check_columns() { catch { int c = outfd->tcgetattr()->columns; if(c) columns = c; }; if(!columns) columns = term->tgetnum("co") || 80; } int get_number_of_columns() { return columns; }
ec8a231999-04-29Fredrik Hübinette (Hubbe)  static string escapify(string s, void|int hide) { #if 1 s=replace(s, ({ "\000","\001","\002","\003","\004","\005","\006","\007", "\010","\011","\012","\013","\014","\015","\016","\017",
0c06b31999-06-09Marcus Comstedt  "\020","\021","\022","\023","\024","\025","\026","\027", "\030","\031","\032","\033","\034","\035","\036","\037",
ec8a231999-04-29Fredrik Hübinette (Hubbe)  "\177", "\200","\201","\202","\203","\204","\205","\206","\207", "\210","\211","\212","\213","\214","\215","\216","\217", "\220","\221","\222","\223","\224","\225","\226","\227", "\230","\231","\232","\233","\234","\235","\236","\237", }), ({ "^@","^A","^B","^C","^D","^E","^F","^G", "^H","^I","^J","^K","^L","^M","^N","^O",
0c06b31999-06-09Marcus Comstedt  "^P","^Q","^R","^S","^T","^U","^V","^W", "^X","^Y","^Z","^[","^\\","^]","^^","^_",
ec8a231999-04-29Fredrik Hübinette (Hubbe)  "^?", "~@","~A","~B","~C","~D","~E","~F","~G", "~H","~I","~J","~K","~L","~M","~N","~O", "~P","~Q","~R","~S","~T","~U","~V","~W", "~X","~Y","~Z","~[","~\\","~]","~^","~_", })); return hide ? "*"*strlen(s) : s; #else for(int i=0; i<strlen(s); i++)
f9322c1999-03-13Marcus Comstedt  if(s[i]<' ') s = s[..i-1]+sprintf("^%c", s[i]+'@')+s[i+1..]; else if(s[i]==127) s = s[..i-1]+"^?"+s[i+1..]; else if(s[i]>=128 && s[i]<160) s = s[..i-1]+sprintf("~%c", s[i]-128+'@')+s[i+1..]; return s;
ec8a231999-04-29Fredrik Hübinette (Hubbe) #endif
f9322c1999-03-13Marcus Comstedt  }
ec8a231999-04-29Fredrik Hübinette (Hubbe) 
f9322c1999-03-13Marcus Comstedt  static int width(string s) { return strlen(s); } static int escapified_width(string s) { return width(escapify(s)); }
0c06b31999-06-09Marcus Comstedt  void low_write(string s, void|int word_break)
f9322c1999-03-13Marcus Comstedt  { int n = width(s); if(!n) return;
ec8a231999-04-29Fredrik Hübinette (Hubbe) 
69d8bc1999-06-22Marcus Comstedt  if(needed_attributes) low_enable_attributes();
ec8a231999-04-29Fredrik Hübinette (Hubbe) // werror("low_write(%O)\n",s); if(word_break) { while(xpos+n>=columns) { int l = columns-xpos; string line=s[..l-1]; int spos=search(reverse(line)," "); if(spos==-1) { outfd->write(line); }else{ l=strlen(line)-spos; outfd->write(line[..l-2]); } s=s[l..]; n-=l;
20f7a71999-06-09Fredrik Hübinette (Hubbe)  xpos+=l; if(xpos<columns || !term->tgetflag("am"))
ec8a231999-04-29Fredrik Hübinette (Hubbe)  outfd->write((term->put("cr")||"")+(term->put("do")||"\n")); xpos = 0; } }else{ while(xpos+n>=columns) { int l = columns-xpos; outfd->write(s[..l-1]); s=s[l..]; n-=l; xpos = 0; if(!term->tgetflag("am")) outfd->write((term->put("cr")||"")+(term->put("do")||"\n")); }
f9322c1999-03-13Marcus Comstedt  }
bd8e291999-03-23Marcus Comstedt  string le;
f9322c1999-03-13Marcus Comstedt  if(n>0) { outfd->write(s); xpos += n;
0c06b31999-06-09Marcus Comstedt  } else if(xpos==0 && term->tgetflag("am") && (le=term->put("le"))) outfd->write(" "+le);
f9322c1999-03-13Marcus Comstedt  }
0c06b31999-06-09Marcus Comstedt  void write(string s,void|int word_break,void|int hide)
f9322c1999-03-13Marcus Comstedt  {
0c06b31999-06-09Marcus Comstedt  low_write(escapify(s,hide),word_break);
f9322c1999-03-13Marcus Comstedt  } void low_move_downward(int n) { if(n<=0) return;
69d8bc1999-06-22Marcus Comstedt  if(active_attributes && !term->tgetflag("ms")) low_disable_attributes();
f9322c1999-03-13Marcus Comstedt  outfd->write(term->put("DO", n) || (term->put("do")||"")*n); } void low_move_upward(int n) { if(n<=0) return;
69d8bc1999-06-22Marcus Comstedt  if(active_attributes && !term->tgetflag("ms")) low_disable_attributes();
f9322c1999-03-13Marcus Comstedt  outfd->write(term->put("UP", n) || (term->put("up")||"")*n); } void low_move_forward(int n) { if(n<=0) return;
69d8bc1999-06-22Marcus Comstedt  if(active_attributes && !term->tgetflag("ms")) low_disable_attributes();
f9322c1999-03-13Marcus Comstedt  if(xpos+n<columns) {
81a6491999-10-21Marcus Comstedt  outfd->write(term->put("RI", n) || (term->put("nd")||term->put("ri")||"")*n);
f9322c1999-03-13Marcus Comstedt  xpos += n; } else { int l = (xpos+n)/columns; low_move_downward(l); n -= l*columns; if(n<0) low_move_backward(-n); else if(n>0) low_move_forward(n); } } void low_move_backward(int n) { if(n<=0) return;
69d8bc1999-06-22Marcus Comstedt  if(active_attributes && !term->tgetflag("ms")) low_disable_attributes();
f9322c1999-03-13Marcus Comstedt  if(xpos-n>=0) {
ff62d71999-03-23Marcus Comstedt  outfd->write(term->put("LE", n) || (term->put("le")||"")*n);
f9322c1999-03-13Marcus Comstedt  xpos -= n; } else { int l = 1+(n-xpos-1)/columns; low_move_upward(l); n -= l*columns; if(n<0) low_move_forward(-n); else if(n>0) low_move_backward(n); } } void low_erase(int n) { string e = term->put("ec", n);
69d8bc1999-06-22Marcus Comstedt  if(active_attributes && !term->tgetflag("ms")) low_disable_attributes();
f9322c1999-03-13Marcus Comstedt  if (e) outfd->write(e); else { low_write(" "*n); low_move_backward(n); } } void move_forward(string s) { low_move_forward(escapified_width(s)); } void move_backward(string s) { low_move_backward(escapified_width(s)); } void erase(string s) { low_erase(escapified_width(s)); } void newline() {
ff62d71999-03-23Marcus Comstedt  string cr = term->put("cr"), down = term->put("do");
69d8bc1999-06-22Marcus Comstedt  if(active_attributes && !term->tgetflag("ms")) low_disable_attributes();
ff62d71999-03-23Marcus Comstedt  if(cr && down) outfd->write(cr+down); else // In this case we have ONLCR (hopefully) outfd->write("\n");
f9322c1999-03-13Marcus Comstedt  xpos = 0; } void bol() {
69d8bc1999-06-22Marcus Comstedt  if(active_attributes && !term->tgetflag("ms")) low_disable_attributes();
ff62d71999-03-23Marcus Comstedt  outfd->write(term->put("cr")||"");
f9322c1999-03-13Marcus Comstedt  xpos = 0; } void clear(int|void partial) { string s;
69d8bc1999-06-22Marcus Comstedt  if(active_attributes) low_disable_attributes();
f9322c1999-03-13Marcus Comstedt  if(!partial && (s = term->put("cl"))) { outfd->write(s); xpos = 0; return; } if(!partial) {
ff62d71999-03-23Marcus Comstedt  outfd->write(term->put("ho")||term->put("cm", 0, 0)||"");
f9322c1999-03-13Marcus Comstedt  xpos = 0; }
ff62d71999-03-23Marcus Comstedt  outfd->write(term->put("cd")||"");
f9322c1999-03-13Marcus Comstedt  }
bbf84a1999-03-18Marcus Comstedt  void beep() { outfd->write(term->put("bl")||""); }
f9322c1999-03-13Marcus Comstedt  void create(object|void _outfd, object|string|void _term) { outfd = _outfd || Stdio.File("stdout"); term = objectp(_term)? _term : .Terminfo.getTerm(_term);
ff62d71999-03-23Marcus Comstedt  catch { oldattrs = outfd->tcgetattr(); };
f9322c1999-03-13Marcus Comstedt  check_columns(); } } class InputController { static private object infd, term; static private int enabled = -1; static private function(:int) close_callback = 0; static private string prefix=""; static private mapping(int:function|mapping(string:function)) bindings=([]); static private function grab_binding = 0; static private mapping oldattrs = 0;
b090761999-06-11Marcus Comstedt  int dumb=0;
f9322c1999-03-13Marcus Comstedt  void destroy() { catch{ infd->set_blocking(); };
b090761999-06-11Marcus Comstedt  if(dumb) return;
ba064e1999-03-23Marcus Comstedt  catch{ infd->tcsetattr((["ECHO":1,"ICANON":1])); };
ff62d71999-03-23Marcus Comstedt  catch{ if(oldattrs) infd->tcsetattr((["ECHO":0,"ICANON":0,"VEOF":0, "VEOL":0,"VLNEXT":0])&oldattrs); };
f9322c1999-03-13Marcus Comstedt  } static private string process_input(string s) { int i; for (i=0; i<sizeof(s); i++) { if (!enabled) return s[i..]; function|mapping(string:function) b = grab_binding || bindings[s[i]]; grab_binding = 0; if (!b) /* do nothing */; else if(mappingp(b)) { int ml = 0, l = sizeof(s)-i; string m; foreach (indices(b), string k) { if (sizeof(k)>l && k[..l-1]==s[i..]) /* Valid prefix, need more data */ return s[i..]; else if (sizeof(k) > ml && s[i..i+sizeof(k)-1] == k) { /* New longest match found */ ml = sizeof(m = k); } } if (ml) { i += ml-1; b[m](m); } } else b(s[i..i]); } return ""; } static private void read_cb(mixed _, string s) { if (!s || s=="") return; if (sizeof(prefix)) { s = prefix+s; prefix = ""; } prefix = process_input(s); } static private void close_cb() { if (close_callback && close_callback()) return; destruct(this_object()); } static private int set_enabled(int e) { if (e != enabled) { enabled = e; if (enabled) { string oldprefix = prefix; prefix = ""; prefix = process_input(oldprefix); infd->set_nonblocking(read_cb, 0, close_cb); } else infd->set_blocking(); return !enabled; } else return enabled; } int isenabled() { return enabled; } int enable(int ... e) { if (sizeof(e)) return set_enabled(!!e[0]); else return set_enabled(1); } int disable() { return set_enabled(0); }
74faa41999-03-23Marcus Comstedt  int run_blocking()
f9322c1999-03-13Marcus Comstedt  { disable(); string data = prefix; prefix = ""; enabled = 1; for (;;) { prefix = process_input(data); if (!enabled)
74faa41999-03-23Marcus Comstedt  return 0;
f9322c1999-03-13Marcus Comstedt  data = prefix+infd->read(1024, 1); prefix = "";
ba064e1999-03-23Marcus Comstedt  if(!data || !sizeof(data)) { disable();
74faa41999-03-23Marcus Comstedt  return -1;
ba064e1999-03-23Marcus Comstedt  }
f9322c1999-03-13Marcus Comstedt  } } void set_close_callback(function (:int) ccb) { close_callback = ccb; } void nullbindings() { bindings = ([]); } void grabnextkey(function g) { if(functionp(g)) grab_binding = g; } function bindstr(string str, function f) { function oldf = 0; if (mappingp(f)) f = 0; // Paranoia switch (sizeof(str||"")) { case 0: break; case 1: if (mappingp(bindings[str[0]])) { oldf = bindings[str[0]][str]; if (f) bindings[str[0]][str] = f; else m_delete(bindings[str[0]], str); } else { oldf = bindings[str[0]]; if (f) bindings[str[0]] = f; else m_delete(bindings, str[0]); } break; default: if (mappingp(bindings[str[0]])) oldf = bindings[str[0]][str]; else bindings[str[0]] = bindings[str[0]]? ([str[0..0]:bindings[str[0]]]) : ([]); if (f) bindings[str[0]][str] = f; else { m_delete(bindings[str[0]], str); if (!sizeof(bindings[str[0]])) m_delete(bindings, str[0]); else if(sizeof(bindings[str[0]])==1 && bindings[str[0]][str[0..0]]) bindings[str[0]] = bindings[str[0]][str[0..0]]; } break; } return oldf; } function unbindstr(string str) { return bindstr(str, 0); } function getbindingstr(string str) { switch (sizeof(str||"")) { case 0: return 0; case 1: return mappingp(bindings[str[0]])? bindings[str[0]][str] : bindings[str[0]]; default: return mappingp(bindings[str[0]]) && bindings[str[0]][str]; } } function bindtc(string cap, function f) { return bindstr(term->tgetstr(cap), f); } function unbindtc(string cap) { return unbindstr(term->tgetstr(cap)); } function getbindingtc(string cap) { return getbindingstr(term->tgetstr(cap)); } string parsekey(string k) { if (k[..1]=="\\!") k = term->tgetstr(k[2..]); else for(int i=0; i<sizeof(k); i++) switch(k[i]) { case '\\': if(i<sizeof(k)-1) switch(k[i+1]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': int n, l; if (2<sscanf(k[i+1..], "%o%n", n, l)) { n = k[i+1]-'0'; l = 1; } k = k[..i-1]+sprintf("%c", n)+k[i+1+l..]; break; case 'e': case 'E': k = k[..i-1]+"\033"+k[i+2..]; break; case 'n': k = k[..i-1]+"\n"+k[i+2..]; break; case 'r': k = k[..i-1]+"\r"+k[i+2..]; break; case 't': k = k[..i-1]+"\t"+k[i+2..]; break; case 'b': k = k[..i-1]+"\b"+k[i+2..]; break; case 'f': k = k[..i-1]+"\f"+k[i+2..]; break; default: k = k[..i-1]+k[i+1..]; break; } break; case '^': if(i<sizeof(k)-1 && k[i+1]>='?' && k[i+1]<='_') k = k[..i-1]+(k[i+1]=='?'? "\177":sprintf("%c",k[i+1]-'@'))+k[i+2..]; break; } return k; } function bind(string k, function f) { return bindstr(parsekey(k), f); } function unbind(string k) { return unbindstr(parsekey(k)); } function getbinding(string k, string cap) { return getbindingstr(parsekey(k)); } mapping(string:function) getbindings() { mapping(int:function) bb = bindings-Array.filter(bindings, mappingp); return `|(mkmapping(Array.map(indices(bb), lambda(int n) { return sprintf("%c", n); }), values(bb)), @Array.filter(values(bindings), mappingp)); } void create(object|void _infd, object|string|void _term) { infd = _infd || Stdio.File("stdin"); term = objectp(_term)? _term : .Terminfo.getTerm(_term); disable();
b090761999-06-11Marcus Comstedt  if(search(term->aliases, "dumb")>=0) { // Dumb terminal. Don't try anything fancy. dumb = 1; return; }
f9322c1999-03-13Marcus Comstedt  catch { oldattrs = infd->tcgetattr(); };
b090761999-06-11Marcus Comstedt  if(catch { infd->tcsetattr((["ECHO":0])); }) { // If echo can't be disabled, Readline won't work very well. // Go to dumb mode. dumb = 1; return; }
f9322c1999-03-13Marcus Comstedt  catch { infd->tcsetattr((["ECHO":0,"ICANON":0,"VMIN":1,"VTIME":0, "VLNEXT":0])); }; } } class DefaultEditKeys {
2326621999-04-26Henrik Grubbström (Grubba)  static private multiset word_break_chars = mkmultiset("\t \n\r/*?_-.[]~&;\!#$%^(){}<>\"'`"/"");
f9322c1999-03-13Marcus Comstedt  static object _readline; void self_insert_command(string str) { _readline->insert(str, _readline->getcursorpos()); } void quoted_insert() { _readline->get_input_controller()->grabnextkey(self_insert_command); } void newline() { _readline->newline(); } void up_history() { _readline->delta_history(-1); } void down_history() { _readline->delta_history(1); } void backward_delete_char() { int p = _readline->getcursorpos(); _readline->delete(p-1,p); } void delete_char_or_eof() { int p = _readline->getcursorpos(); if (p<strlen(_readline->gettext())) _readline->delete(p,p+1); else if(!strlen(_readline->gettext())) _readline->eof(); } void forward_char() { _readline->setcursorpos(_readline->getcursorpos()+1); } void backward_char() { _readline->setcursorpos(_readline->getcursorpos()-1); } void beginning_of_line() { _readline->setcursorpos(0); } void end_of_line() { _readline->setcursorpos(strlen(_readline->gettext())); } void transpose_chars() { int p = _readline->getcursorpos(); if (p<0 || p>=strlen(_readline->gettext())) return; string c = _readline->gettext()[p-1..p]; _readline->delete(p-1, p+1); _readline->insert(reverse(c), p-1); }
f566a01999-04-25Marcus Comstedt  static array find_word_to_manipulate()
400e7e1999-04-25David Hedbor  { int p = _readline->getcursorpos(); int ep; string line = _readline->gettext(); while(word_break_chars[ line[p..p] ] && p < strlen(line)) p++; if(p >= strlen(line)) { _readline->setcursorpos(p); return ({ 0, 0 }); } ep = forward_find_word(); _readline->delete(p, ep); return ({ line[p..ep-1], p }); } void capitalize_word() { [string word, string pos]= find_word_to_manipulate(); if(word) _readline->insert(String.capitalize(lower_case(word)), pos); }
f566a01999-04-25Marcus Comstedt  void upcase_word()
400e7e1999-04-25David Hedbor  { [string word, string pos]= find_word_to_manipulate(); if(word) _readline->insert(upper_case(word), pos); }
f566a01999-04-25Marcus Comstedt  void downcase_word()
400e7e1999-04-25David Hedbor  { [string word, string pos]= find_word_to_manipulate(); if(word) _readline->insert(lower_case(word), pos); }
f566a01999-04-25Marcus Comstedt  static int forward_find_word()
53fa181999-04-02David Hedbor  { int p, n; string line = _readline->gettext(); for(p = _readline->getcursorpos(); p < sizeof(line); p++) { if(word_break_chars[ line[p..p] ]) { if(n) break; } else n = 1; } return p; }
f566a01999-04-25Marcus Comstedt  static int backward_find_word()
53fa181999-04-02David Hedbor  {
400e7e1999-04-25David Hedbor  int p = _readline->getcursorpos()-1;
53fa181999-04-02David Hedbor  string line = _readline->gettext(); if(p >= strlen(line)) p = strlen(line) - 1;
400e7e1999-04-25David Hedbor  while(word_break_chars[ line[p..p] ] && p >= 0) // find first "non break char" p--; for(;p >= 0; p--)
53fa181999-04-02David Hedbor  if(word_break_chars[ line[p..p] ]) {
400e7e1999-04-25David Hedbor  p++; // We want to be one char before the break char. break; }
53fa181999-04-02David Hedbor  return p; } void forward_word() { _readline->setcursorpos(forward_find_word()); } void backward_word() { _readline->setcursorpos(backward_find_word()); }
8e317d1999-06-06Mirar (Pontus Hagland)  void kill_word()
53fa181999-04-02David Hedbor  {
8e317d1999-06-06Mirar (Pontus Hagland)  _readline->kill(_readline->getcursorpos(), forward_find_word());
53fa181999-04-02David Hedbor  }
400e7e1999-04-25David Hedbor 
8e317d1999-06-06Mirar (Pontus Hagland)  void backward_kill_word()
53fa181999-04-02David Hedbor  {
400e7e1999-04-25David Hedbor  int sp = backward_find_word();
6dd2821999-04-02David Hedbor  int ep = _readline->getcursorpos(); if((ep - sp) == 0) sp--;
8e317d1999-06-06Mirar (Pontus Hagland)  _readline->kill(sp, ep);
53fa181999-04-02David Hedbor  }
f9322c1999-03-13Marcus Comstedt  void kill_line() {
8e317d1999-06-06Mirar (Pontus Hagland)  _readline->kill(_readline->getcursorpos(), strlen(_readline->gettext()));
f9322c1999-03-13Marcus Comstedt  } void kill_whole_line() {
8e317d1999-06-06Mirar (Pontus Hagland)  _readline->kill(0, strlen(_readline->gettext())); } void yank() { _readline->setmark(_readline->getcursorpos()); _readline->insert(_readline->kill_ring_yank(),_readline->getcursorpos()); } void kill_ring_save() { _readline->add_to_kill_ring(_readline->region()); } void kill_region() { _readline->kill(@_readline->pointmark()); } void set_mark() { _readline->setmark(_readline->getcursorpos()); } void swap_mark_and_point() { int p=_readline->getcursorpos(); _readline->setcursorpos(_readline->getmark()); _readline->setmark(p);
f9322c1999-03-13Marcus Comstedt  } void redisplay() { _readline->redisplay(0); } void clear_screen() { _readline->redisplay(1); } static array(array(string|function)) default_bindings = ({ ({ "^[[A", up_history }), ({ "^[[B", down_history }), ({ "^[[C", forward_char }), ({ "^[[D", backward_char }),
f566a01999-04-25Marcus Comstedt  ({ "^[C", capitalize_word }), ({ "^[c", capitalize_word }), ({ "^[U", upcase_word }), ({ "^[u", upcase_word }), ({ "^[L", downcase_word }), ({ "^[l", downcase_word }),
8e317d1999-06-06Mirar (Pontus Hagland)  ({ "^[D", kill_word }), ({ "^[^H", backward_kill_word }), ({ "^[^?", backward_kill_word }), ({ "^[d", kill_word }),
f566a01999-04-25Marcus Comstedt  ({ "^[F", forward_word }), ({ "^[B", backward_word }), ({ "^[f", forward_word }), ({ "^[b", backward_word }),
8e317d1999-06-06Mirar (Pontus Hagland)  ({ "^[w", kill_ring_save }), ({ "^[W", kill_ring_save }), ({ "^0", set_mark }),
f9322c1999-03-13Marcus Comstedt  ({ "^A", beginning_of_line }), ({ "^B", backward_char }), ({ "^D", delete_char_or_eof }), ({ "^E", end_of_line }), ({ "^F", forward_char }), ({ "^H", backward_delete_char }), ({ "^J", newline }), ({ "^K", kill_line }), ({ "^L", clear_screen }), ({ "^M", newline }), ({ "^N", down_history }), ({ "^P", up_history }), ({ "^R", redisplay }), ({ "^T", transpose_chars }), ({ "^U", kill_whole_line }), ({ "^V", quoted_insert }),
8e317d1999-06-06Mirar (Pontus Hagland)  ({ "^W", kill_region }), ({ "^Y", yank }),
f9322c1999-03-13Marcus Comstedt  ({ "^?", backward_delete_char }), ({ "\\!ku", up_history }), ({ "\\!kd", down_history }), ({ "\\!kr", forward_char }),
53fa181999-04-02David Hedbor  ({ "\\!kl", backward_char }),
8e317d1999-06-06Mirar (Pontus Hagland)  ({ "^X^X", swap_mark_and_point }),
f9322c1999-03-13Marcus Comstedt  }); static void set_default_bindings() { object ic = _readline->get_input_controller(); ic->nullbindings(); for(int i=' '; i<'\177'; i++) ic->bindstr(sprintf("%c", i), self_insert_command); for(int i='\240'; i<='\377'; i++) ic->bindstr(sprintf("%c", i), self_insert_command);
b090761999-06-11Marcus Comstedt  if(ic->dumb) { ic->bind("^J", newline); return; }
6dd2821999-04-02David Hedbor 
f9322c1999-03-13Marcus Comstedt  foreach(default_bindings, array(string|function) b) ic->bind(@b); } void create(object readline) { _readline = readline; set_default_bindings(); } } class History { static private array(string) historylist;
ec8a231999-04-29Fredrik Hübinette (Hubbe)  static private mapping(int:string) historykeep=([]);
f9322c1999-03-13Marcus Comstedt  static private int minhistory, maxhistory, historynum;
ce5a241999-10-04Johan Schön  array(string) encode() { return historylist; }
f9322c1999-03-13Marcus Comstedt  int get_history_num() { return historynum; } string history(int n, string text) { if (n<minhistory) n = minhistory; else if (n-minhistory>=sizeof(historylist)) n = sizeof(historylist)+minhistory-1;
657ade1999-03-14Marcus Comstedt  if(text != historylist[historynum-minhistory]) { if(!historykeep[historynum]) historykeep[historynum] = historylist[historynum-minhistory]; historylist[historynum-minhistory]=text; }
f9322c1999-03-13Marcus Comstedt  return historylist[(historynum=n)-minhistory]; } void initline() { if (sizeof(historylist)==0 || historylist[-1]!="") { historylist += ({ "" }); if (maxhistory && sizeof(historylist)>maxhistory) { int n = sizeof(historylist)-maxhistory; historylist = historylist[n..]; minhistory += n; } } historynum = sizeof(historylist)-1+minhistory;
657ade1999-03-14Marcus Comstedt  historykeep = ([]);
f9322c1999-03-13Marcus Comstedt  } void finishline(string text) {
657ade1999-03-14Marcus Comstedt  foreach(indices(historykeep), int n) historylist[n-minhistory]=historykeep[n];
ec8a231999-04-29Fredrik Hübinette (Hubbe)  historykeep = ([]);
657ade1999-03-14Marcus Comstedt  historylist[-1] = text; if(sizeof(historylist)>1 && historylist[-2]==historylist[-1]) historylist = historylist[..sizeof(historylist)-2];
f9322c1999-03-13Marcus Comstedt  } void set_max_history(int maxhist) { maxhistory = maxhist; }
ce5a241999-10-04Johan Schön  void create(int maxhist, void|array(string) hist)
f9322c1999-03-13Marcus Comstedt  {
ce5a241999-10-04Johan Schön  historylist = hist || ({ "" });
f9322c1999-03-13Marcus Comstedt  minhistory = historynum = 0; maxhistory = maxhist; } }
1933461999-03-15Marcus Comstedt static private object(OutputController) output_controller; static private object(InputController) input_controller; static private string prompt=""; static private string text="", readtext; static private function(string:void) newline_func; static private int cursorpos = 0;
8e317d1999-06-06Mirar (Pontus Hagland) static private int mark = 0;
ce5a241999-10-04Johan Schön /*static private */ object(History) historyobj = 0;
ec8a231999-04-29Fredrik Hübinette (Hubbe) static private int hide = 0;
f9322c1999-03-13Marcus Comstedt 
8e317d1999-06-06Mirar (Pontus Hagland) static private array(string) kill_ring=({}); static private int kill_ring_size=30;
1933461999-03-15Marcus Comstedt object(OutputController) get_output_controller() { return output_controller; }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt object(InputController) get_input_controller() { return input_controller; }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt string get_prompt() { return prompt; }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt string set_prompt(string newp) { string oldp = prompt;
ec8a231999-04-29Fredrik Hübinette (Hubbe)  if(newp!=prompt) {
d3a6761999-04-30Fredrik Hübinette (Hubbe)  if(newline_func)
ec8a231999-04-29Fredrik Hübinette (Hubbe)  {
5797801999-06-04Fredrik Hübinette (Hubbe) // werror("\nNew prompt!!! %O\n",newline_func);
ec8a231999-04-29Fredrik Hübinette (Hubbe)  int p=cursorpos; setcursorpos(0); output_controller->bol(); output_controller->clear(1); prompt = newp; cursorpos=strlen(text); redisplay(0, 1); cursorpos=p; }else{ prompt = newp; } }
1933461999-03-15Marcus Comstedt  return oldp; }
f9322c1999-03-13Marcus Comstedt 
ec8a231999-04-29Fredrik Hübinette (Hubbe) void set_echo(int onoff) { hide=!onoff; }
1933461999-03-15Marcus Comstedt string gettext() { return text; }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt int getcursorpos() { return cursorpos; }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt int setcursorpos(int p) { if (p<0) p = 0; if (p>strlen(text)) p = strlen(text); if (p<cursorpos)
f9322c1999-03-13Marcus Comstedt  {
b090761999-06-11Marcus Comstedt  if(!input_controller->dumb) output_controller->move_backward(text[p..cursorpos-1]);
1933461999-03-15Marcus Comstedt  cursorpos = p;
f9322c1999-03-13Marcus Comstedt  }
1933461999-03-15Marcus Comstedt  else if (p>cursorpos)
f9322c1999-03-13Marcus Comstedt  {
b090761999-06-11Marcus Comstedt  if(!input_controller->dumb) output_controller->move_forward(text[cursorpos..p-1]);
1933461999-03-15Marcus Comstedt  cursorpos = p;
f9322c1999-03-13Marcus Comstedt  }
1933461999-03-15Marcus Comstedt  return cursorpos; }
f9322c1999-03-13Marcus Comstedt 
8e317d1999-06-06Mirar (Pontus Hagland) int setmark(int p) { if (p<0) p = 0; if (p>strlen(text)) p = strlen(text); mark=p; } int getmark() { return mark; }
1933461999-03-15Marcus Comstedt void insert(string s, int p) { if (p<0) p = 0; if (p>strlen(text)) p = strlen(text); setcursorpos(p);
b090761999-06-11Marcus Comstedt  if(!input_controller->dumb) output_controller->write(s,0,hide);
1933461999-03-15Marcus Comstedt  cursorpos += strlen(s); string rest = text[p..];
b090761999-06-11Marcus Comstedt  if (strlen(rest) && !input_controller->dumb)
1933461999-03-15Marcus Comstedt  {
0c06b31999-06-09Marcus Comstedt  output_controller->write(rest,0,hide);
1933461999-03-15Marcus Comstedt  output_controller->move_backward(rest); } text = text[..p-1]+s+rest;
8e317d1999-06-06Mirar (Pontus Hagland)  if (mark>p) mark+=strlen(s);
1933461999-03-15Marcus Comstedt }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt void delete(int p1, int p2) { if (p1<0) p1 = 0; if (p2>strlen(text)) p2 = strlen(text); setcursorpos(p1); if (p1>=p2) return;
b090761999-06-11Marcus Comstedt  if(!input_controller->dumb) { output_controller->write(text[p2..],0,hide); output_controller->erase(text[p1..p2-1]); }
1933461999-03-15Marcus Comstedt  text = text[..p1-1]+text[p2..];
8e317d1999-06-06Mirar (Pontus Hagland)  if (mark>p2) mark-=(p2-p1); else if (mark>p1) mark=p1;
1933461999-03-15Marcus Comstedt  cursorpos = strlen(text); setcursorpos(p1); }
f9322c1999-03-13Marcus Comstedt 
8e317d1999-06-06Mirar (Pontus Hagland) array(int) pointmark() // returns point and mark in numeric order { int p1,p2; p1=getcursorpos(),p2=getmark(); if (p1>p2) return ({p2,p1}); return ({p1,p2}); } string region(int ... args) /* p1, p2 or point-mark */ { int p1,p2; if (sizeof(args)) [p1,p2]=args; else [p1,p2]=pointmark(); return text[p1..p2-1]; } void kill(int p1, int p2) { if (p1<0) p1 = 0; if (p2>strlen(text)) p2 = strlen(text); if (p1>=p2) return; add_to_kill_ring(text[p1..p2-1]); delete(p1,p2); } void add_to_kill_ring(string s) { kill_ring+=({s}); if (sizeof(kill_ring)>kill_ring_size) kill_ring=kill_ring[1..]; } string kill_ring_yank() { if (!sizeof(kill_ring)) return ""; return kill_ring[-1]; }
1933461999-03-15Marcus Comstedt void history(int n) { if(historyobj) { string h = historyobj->history(n, text);
ec8a231999-04-29Fredrik Hübinette (Hubbe)  delete(0, strlen(text)+strlen(prompt));
1933461999-03-15Marcus Comstedt  insert(h, 0);
f9322c1999-03-13Marcus Comstedt  }
1933461999-03-15Marcus Comstedt }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt void delta_history(int d) { if(historyobj) history(historyobj->get_history_num()+d); }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt void redisplay(int clear, int|void nobackup) { int p = cursorpos; if(clear) output_controller->clear(); else if(!nobackup) { setcursorpos(0); output_controller->bol(); output_controller->clear(1); } output_controller->check_columns();
ec8a231999-04-29Fredrik Hübinette (Hubbe) 
b090761999-06-11Marcus Comstedt  if(!input_controller->dumb) {
41a4331999-06-22Marcus Comstedt  if(newline_func == read_newline)
b090761999-06-11Marcus Comstedt  output_controller->write(prompt); output_controller->write(text,0,hide); }
1933461999-03-15Marcus Comstedt  cursorpos = sizeof(text); setcursorpos(p); }
17bc0b1999-03-14Marcus Comstedt 
1933461999-03-15Marcus Comstedt static private void initline() { text = ""; cursorpos = 0; if (historyobj) historyobj->initline(); }
17bc0b1999-03-14Marcus Comstedt 
1933461999-03-15Marcus Comstedt string newline() { setcursorpos(sizeof(text));
b090761999-06-11Marcus Comstedt  if(!input_controller->dumb) output_controller->newline(); else output_controller->bol();
1933461999-03-15Marcus Comstedt  string data = text;
ec8a231999-04-29Fredrik Hübinette (Hubbe)  if (historyobj && !hide)
1933461999-03-15Marcus Comstedt  historyobj->finishline(text); initline(); if(newline_func) newline_func(data); }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt void eof() { if (historyobj) historyobj->finishline(text); initline(); if(newline_func) newline_func(0); }
f9322c1999-03-13Marcus Comstedt 
ec8a231999-04-29Fredrik Hübinette (Hubbe) 
1933461999-03-15Marcus Comstedt void message(string msg) { int p = cursorpos; setcursorpos(strlen(text)); output_controller->newline(); foreach(msg/"\n", string l) { output_controller->write(l); output_controller->newline();
f9322c1999-03-13Marcus Comstedt  }
1933461999-03-15Marcus Comstedt  redisplay(0, 1); setcursorpos(p); }
f9322c1999-03-13Marcus Comstedt 
ec8a231999-04-29Fredrik Hübinette (Hubbe) void write(string msg,void|int word_wrap) { int p = cursorpos; setcursorpos(0);
b090761999-06-11Marcus Comstedt  if(!input_controller->dumb) { output_controller->bol(); output_controller->clear(1); }
ec8a231999-04-29Fredrik Hübinette (Hubbe)  array(string) tmp=msg/"\n"; foreach(tmp[..sizeof(tmp)-2],string l) {
0c06b31999-06-09Marcus Comstedt  output_controller->write(l,word_wrap);
ec8a231999-04-29Fredrik Hübinette (Hubbe)  output_controller->newline(); }
0c06b31999-06-09Marcus Comstedt  output_controller->write(tmp[-1],word_wrap);
ec8a231999-04-29Fredrik Hübinette (Hubbe)  cursorpos=strlen(text); redisplay(0, 1); setcursorpos(p); }
1933461999-03-15Marcus Comstedt void list_completions(array(string) c) {
ec8a231999-04-29Fredrik Hübinette (Hubbe)  message(sprintf("%-*#s",output_controller->get_number_of_columns(),
1933461999-03-15Marcus Comstedt  c*"\n")); }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt static private void read_newline(string s) { input_controller->disable(); readtext = s; }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt void set_nonblocking(function f) {
ff62d71999-03-23Marcus Comstedt  if (newline_func = f) { output_controller->enable();
1933461999-03-15Marcus Comstedt  input_controller->enable();
ff62d71999-03-23Marcus Comstedt  } else {
1933461999-03-15Marcus Comstedt  input_controller->disable();
ff62d71999-03-23Marcus Comstedt  output_controller->disable(); }
f9322c1999-03-13Marcus Comstedt }
1933461999-03-15Marcus Comstedt void set_blocking() { set_nonblocking(0); }
f9322c1999-03-13Marcus Comstedt 
5b4d991999-06-09Marcus Comstedt string edit(string data, string|void local_prompt)
1933461999-03-15Marcus Comstedt {
e6ba311999-09-10Fredrik Noring  string old_prompt;
1933461999-03-15Marcus Comstedt  if(newline_func == read_newline) return 0;
e6ba311999-09-10Fredrik Noring  if(local_prompt) { old_prompt = get_prompt(); set_prompt(local_prompt); }
1933461999-03-15Marcus Comstedt  function oldnl = newline_func;
5b4d991999-06-09Marcus Comstedt  output_controller->write(local_prompt||prompt);
1933461999-03-15Marcus Comstedt  initline(); newline_func = read_newline; readtext = "";
ff62d71999-03-23Marcus Comstedt  output_controller->enable();
5b4d991999-06-09Marcus Comstedt  insert(data, 0);
74faa41999-03-23Marcus Comstedt  int res = input_controller->run_blocking();
ce5a241999-10-04Johan Schön 
1933461999-03-15Marcus Comstedt  set_nonblocking(oldnl);
e6ba311999-09-10Fredrik Noring  if(local_prompt) set_prompt(old_prompt);
74faa41999-03-23Marcus Comstedt  return (res>=0 || sizeof(readtext)) && readtext;
1933461999-03-15Marcus Comstedt }
f9322c1999-03-13Marcus Comstedt 
5b4d991999-06-09Marcus Comstedt string read(string|void prompt) { return edit("", prompt); }
ce5a241999-10-04Johan Schön void enable_history(array(string)|object(History)|int hist)
1933461999-03-15Marcus Comstedt { if (objectp(hist)) historyobj = hist;
ce5a241999-10-04Johan Schön  else if(arrayp(hist)) historyobj = History(512,hist);
1933461999-03-15Marcus Comstedt  else if(!hist) historyobj = 0; else if(historyobj) historyobj->set_max_history(hist); else historyobj = History(hist); }
f9322c1999-03-13Marcus Comstedt 
ce5a241999-10-04Johan Schön History get_history() { return historyobj; }
1933461999-03-15Marcus Comstedt void destroy() {
d15fb61999-03-15Marcus Comstedt  if(input_controller) destruct(input_controller); if(output_controller) destruct(output_controller);
1933461999-03-15Marcus Comstedt }
f9322c1999-03-13Marcus Comstedt 
1933461999-03-15Marcus Comstedt void create(object|void infd, object|string|void interm, object|void outfd, object|string|void outterm)
f9322c1999-03-13Marcus Comstedt {
3055d11999-04-11Fredrik Hübinette (Hubbe)  atexit(destroy);
1933461999-03-15Marcus Comstedt  output_controller = OutputController(outfd || infd, outterm || interm); input_controller = InputController(infd, interm); DefaultEditKeys(this_object());
f9322c1999-03-13Marcus Comstedt }
1933461999-03-15Marcus Comstedt