Branch: Tag:

1999-03-13

1999-03-13 01:12:37 by Marcus Comstedt <marcus@mc.pp.se>

Readline related modules added to repository.

Rev: lib/modules/Stdio.pmod/Readline.pmod:1.1
Rev: lib/modules/Stdio.pmod/Terminfo.pmod:1.1

1: + // $Id: Terminfo.pmod,v 1.1 1999/03/13 01:12:37 marcus Exp $    -  +  + #if constant(thread_create) + #define LOCK object m_key = mutex->lock() + #define UNLOCK destruct(m_key) + #define MUTEX static private object mutex = Thread.Mutex(); + #else + #define LOCK + #define UNLOCK + #define MUTEX + #endif +  + MUTEX +  + static private array ctrlcharsfrom = +  Array.map(indices(allocate(32)), +  lambda(int z) { return sprintf("^%c",z+64); })+ +  Array.map(indices(allocate(32)), +  lambda(int z) { return sprintf("^%c",z+96); }); + static private array ctrlcharsto = +  Array.map(indices(allocate(32)), +  lambda(int z) { return sprintf("%c",z); })+ +  Array.map(indices(allocate(32)), +  lambda(int z) { return sprintf("%c",z); }); +  +  + static private class TermMachine { +  +  mapping(string:string|int) map = ([]); +  +  int tgetflag(string id) +  { +  return map[id]==1; +  } +  +  int tgetnum(string id) +  { +  return intp(map[id]) && map[id]; +  } +  +  string tgetstr(string id) +  { +  return stringp(map[id]) && map[id]; +  } +  +  string tparam(string f, mixed ... args) +  { +  array fmt=f/"%"; +  string res=fmt[0]; +  string tmp; +  int z; +  mapping var=([]); +  array args0=args; +  + #define POP (z=args[0],args=args[1..],z) + #define PUSH(x) (args=({x})+args) +  +  while ( (fmt=fmt[1..])!=({}) ) +  if (fmt[0]=="") res+="%"; +  else +  { +  switch (fmt[0][0]) +  { +  case 'd': res+=sprintf("%d%s",POP,fmt[0][1..]); break; +  case 'x': res+=sprintf("%x%s",POP,fmt[0][1..]); break; +  case '0': case '2': case '3': +  sscanf(fmt[0],"%[0-9]%s",tmp,fmt[0]); +  res+=sprintf("%"+tmp+fmt[0][..0]+"%s",POP,fmt[0][1..]); +  break; +  case 'c': res+=sprintf("%c%s",POP,fmt[0][1..]); break; +  case 's': res+=sprintf("%s%s",POP,fmt[0][1..]); break; +  +  case '\'': +  sscanf(fmt[0],"'%s'%s",tmp,fmt[0]); +  if (tmp=="") tmp="\0"; +  if (tmp[0]=='\\') tmp=sprintf("%c",(int)("0"+tmp[1..])); +  PUSH(tmp[0]); +  res+=fmt[0]; +  break; +  case '{': +  sscanf(fmt[0],"{%d}%s",z,fmt[0]); +  res+=fmt[0]; +  PUSH(z); +  break; +  case 'p': +  PUSH(args0[fmt[0][1]-'1']); +  res+=fmt[0][2..]; +  break; +  case 'P': +  var[fmt[0][1]]=POP; +  res+=fmt[0][2..]; +  break; +  case 'g': +  PUSH(var[fmt[0][1]]); +  res+=fmt[0][2..]; +  break; +  case 'i': +  args[0]+=1; +  args[1]+=1; +  break; +  case '+': PUSH(POP+POP); res+=fmt[0][1..]; break; +  case '-': PUSH(POP-POP); res+=fmt[0][1..]; break; +  case '*': PUSH(POP*POP); res+=fmt[0][1..]; break; +  case '/': PUSH(POP/POP); res+=fmt[0][1..]; break; +  case 'm': PUSH(POP%POP); res+=fmt[0][1..]; break; +  case '&': PUSH(POP&POP); res+=fmt[0][1..]; break; +  case '|': PUSH(POP|POP); res+=fmt[0][1..]; break; +  case '^': PUSH(POP^POP); res+=fmt[0][1..]; break; +  case '=': PUSH(POP==POP); res+=fmt[0][1..]; break; +  case '>': PUSH(POP>POP); res+=fmt[0][1..]; break; +  case '<': PUSH(POP<POP); res+=fmt[0][1..]; break; +  case 'A': PUSH(POP && POP); res+=fmt[0][1..]; break; +  case 'O': PUSH(POP || POP); res+=fmt[0][1..]; break; +  case '!': PUSH(!POP); res+=fmt[0][1..]; break; +  case '~': PUSH(~POP); res+=fmt[0][1..]; break; +  case '?': +  error("Sorry, Terminal can't handle if-else's\n"); +  default: +  error("Unknown opcode: %%%s\n",fmt[0][..0]); +  } +  } +  return res; +  } +  +  string tgoto(string cap, int col, int row) +  { +  return tparam(cap, col, row); +  } +  +  string tputs(string s) +  { +  return s; +  } +  +  string put(string cap, mixed ... args) +  { +  string str = tgetstr(cap); +  string tstr = str && tparam(str, @args); +  return tstr && tputs(tstr); +  } +  + } +  + class Termcap { +  +  inherit TermMachine; +  +  array(string) aliases; +  object parent; +  +  string tputs(string s) +  { +  // Delay stuff completely ignored... +  sscanf(s, "%*d%s", s); +  return s; +  } +  +  private static multiset(string) load_cap(string en) +  { +  string br=":"; +  int i=search(en,":"); +  int j=search(en,","); +  multiset(string) clears = (<>); +  +  if (i==-1) { i=j; br=","; } +  else if (j!=-1) { i=min(i,j); if (i==j) br=","; } +  if (i<1) +  error("Termcap: Unparsable entry\n"); +  aliases=en[..i-1]/"|"; +  en=en[i..]; +  +  while (en!="") +  { +  string name; +  string data; +  sscanf(en,"%*[ \t]%[a-zA-Z_0-9&]%s"+br+"%s",name,data,en); +  +  if (data=="") // boolean +  { +  if (name!="") map[name]=1; +  } +  else if (data[0]=='@') // boolean off +  { +  clears[name]=1; +  } +  else if (data[0]=='#') // number +  { +  int z; +  sscanf(data,"#%d",z); +  map[name]=z; +  } +  else if (data[0]=='=') // string +  { +  data=data[1..]; +  while (data[-1]=="\\") +  { +  string add; +  if (sscanf(en,"%s"+br+"%s",add,en)<2) break; +  data+="\\"+add; +  } +  +  data=replace(data,"\\^","\\*"); +  +  if (search(data,"^")!=-1) +  data=replace(data,ctrlcharsfrom,ctrlcharsto); +  +  data = replace(data, +  ({"\\E","\\e","\\n","\\r","\\t","\\b","\\f", +  "\\*","\\\\","\\,","\\:","#", +  "\\0","\\1","\\2","\\3","\\4","\\5","\\6","\\7"}), +  ({"\033","\033","\n","\r","\t","\b","\f", +  "^","\\",",",":","#!", +  "#0","#1","#2","#3","#4","#5","#6","#7"})); +  +  array(string) parts = data/"#"; +  data = parts[0]; +  foreach (parts[1..], string p) +  if (sizeof(p) && p[0]=='!') +  data += "#"+p[1..]; +  else +  { +  int n; +  string x; +  if(2==sscanf(p[..2], "%o%s", n, x)) +  data += sprintf("%c%s%s", n, x, p[3..]); +  else +  data += p; +  } +  +  map[name]=data; +  +  } +  else // wierd +  { +  // ignore +  } +  } +  +  return clears; +  } +  +  void create(string cap, object|void tcdb, int|void maxrecurse) +  { +  int i=0; +  while((i=search(cap, "\\\n", i))>=0) { +  string capr; +  if(2!=sscanf(cap[i..], "\\\n%*[ \t\r]%s", capr)) +  break; +  cap = cap[..i-1]+capr; +  } +  multiset(string) clears = load_cap(cap); +  if(map->tc) { +  if(maxrecurse==1) +  error("Termcap: maximum inheritance depth exceeded (loop?)\n"); +  parent = (tcdb||defaultTermcapDB())-> +  load(map->tc, maxrecurse? (maxrecurse-1):25); +  if(!parent) +  error("Termcap: can't find parent terminal \"%s\"\n", map->tc); +  map = parent->map | map; +  } +  map |= mkmapping(indices(clears), allocate(sizeof(clears))); +  } + } +  +  +  + class Terminfo { +  +  inherit TermMachine; +  +  array(string) aliases; +  +  static private constant boolnames = +  ({ "bw","am","xb","xs","xn","eo","gn","hc","km","hs","in","da","db","mi", +  "ms","os","es","xt","hz","ul","xo","nx","5i","HC","NR","NP","ND","cc", +  "ut","hl","YA","YB","YC","YD","YE","YF","YG" }); +  static private constant numnames = +  ({ "co","it","li","lm","sg","pb","vt","ws","Nl","lh","lw","ma","MW","Co", +  "pa","NC","Ya","Yb","Yc","Yd","Ye","Yf","Yg","Yh","Yi","Yj","Yk","Yl", +  "Ym","Yn","BT","Yo","Yp" }); +  static private constant strnames = +  ({ "bt","bl","cr","cs","ct","cl","ce","cd","ch","CC","cm","do","ho","vi", +  "le","CM","ve","nd","ll","up","vs","dc","dl","ds","hd","as","mb","md", +  "ti","dm","mh","im","mk","mp","mr","so","us","ec","ae","me","te","ed", +  "ei","se","ue","vb","ff","fs","i1","is","i3","if","ic","al","ip","kb", +  "ka","kC","kt","kD","kL","kd","kM","kE","kS","k0","k1","k;","k2","k3", +  "k4","k5","k6","k7","k8","k9","kh","kI","kA","kl","kH","kN","kP","kr", +  "kF","kR","kT","ku","ke","ks","l0","l1","la","l2","l3","l4","l5","l6", +  "l7","l8","l9","mo","mm","nw","pc","DC","DL","DO","IC","SF","AL","LE", +  "RI","SR","UP","pk","pl","px","ps","pf","po","rp","r1","r2","r3","rf", +  "rc","cv","sc","sf","sr","sa","st","wi","ta","ts","uc","hu","iP","K1", +  "K3","K2","K4","K5","pO","rP","ac","pn","kB","SX","RX","SA","RA","XN", +  "XF","eA","LO","LF","@1","@2","@3","@4","@5","@6","@7","@8","@9","@0", +  "%1","%2","%3","%4","%5","%6","%7","%8","%9","%0","&1","&2","&3","&4", +  "&5","&6","&7","&8","&9","&0","*1","*2","*3","*4","*5","*6","*7","*8", +  "*9","*0","#1","#2","#3","#4","%a","%b","%c","%d","%e","%f","%g","%h", +  "%i","%j","!1","!2","!3","RF","F1","F2","F3","F4","F5","F6","F7","F8", +  "F9","FA","FB","FC","FD","FE","FF","FG","FH","FI","FJ","FK","FL","FM", +  "FN","FO","FP","FQ","FR","FS","FT","FU","FV","FW","FX","FY","FZ","Fa", +  "Fb","Fc","Fd","Fe","Ff","Fg","Fh","Fi","Fj","Fk","Fl","Fm","Fn","Fo", +  "Fp","Fq","Fr","cb","MC","ML","MR","Lf","SC","DK","RC","CW","WG","HU", +  "DI","QD","TO","PU","fh","PA","WA","u0","u1","u2","u3","u4","u5","u6", +  "u7","u8","u9","op","oc","Ic","Ip","sp","Sf","Sb","ZA","ZB","ZC","ZD", +  "ZE","ZF","ZG","ZH","ZI","ZJ","ZK","ZL","ZM","ZN","ZO","ZP","ZQ","ZR", +  "ZS","ZT","ZU","ZV","ZW","ZX","ZY","ZZ","Za","Zb","Zc","Zd","Ze","Zf", +  "Zg","Zh","Zi","Zj","Zk","Zl","Zm","Zn","Zo","Zp","Zq","Zr","Zs","Zt", +  "Zu","Zv","Zw","Zx","Zy","Km","Mi","RQ","Gm","AF","AB","xl","dv","ci", +  "s0","s1","s2","s3","ML","MT","Xy","Zz","Yv","Yw","Yx","Yy","Yz","YZ", +  "S1","S2","S3","S4","S5","S6","S7","S8","Xh","Xl","Xo","Xr","Xt","Xv", +  "sA","sL" }); +  +  string tputs(string s) +  { +  // Delay stuff completely ignored... +  string pre, post; +  while (3==sscanf(s, "%s$<%*[0-9.]>%s", pre, post)) +  s = pre+post; +  return s; +  } +  +  static private string swab(string s) +  { +  return Array.map(s/2, reverse)*""; +  } +  +  static private int load_cap(object f) +  { +  int magic, sname, nbool, nnum, nstr, sstr; +  +  if (6!=sscanf(swab(f->read(12)), "%2c%2c%2c%2c%2c%2c", +  magic, sname, nbool, nnum, nstr, sstr) || +  magic != 0432) +  return 0; +  aliases = (f->read(sname)-"\0")/"|"; +  { +  array(int) bools = values(f->read(nbool+(nbool&1))[..nbool-1]); +  if (sizeof(bools)>sizeof(boolnames)) +  bools = bools[..sizeof(boolnames)-1]; +  map = mkmapping(boolnames[..sizeof(bools)-1], bools); +  } +  { +  array(int) nums = array_sscanf(swab(f->read(nnum*2)), "%2c"*nnum); +  if (sizeof(nums)>sizeof(numnames)) +  nums = nums[..sizeof(numnames)-1]; +  mapping(string:int) tmp = mkmapping(numnames[..sizeof(nums)-1], nums); +  foreach (numnames[..sizeof(nums)-1], string name) +  if (tmp[name]>=0xfffe) +  m_delete(tmp, name); +  map += tmp; +  } +  { +  string stroffs = swab(f->read(nstr*2)); +  string strbuf = f->read(sstr); +  if(strlen(strbuf)!=sstr) +  return 0; +  array(string) strarr = Array.map(array_sscanf(stroffs, "%2c"*nstr), +  lambda(int offs, string buf) { +  return offs<0xfffe && +  buf[offs.. +  search(buf, "\0", offs)-1]; +  }, strbuf+"\0"); +  if (sizeof(strarr)>sizeof(strnames)) +  strarr = strarr[..sizeof(strnames)-1]; +  mapping(string:string) tmp = mkmapping(strnames[..sizeof(strarr)-1], +  strarr); +  foreach (strnames[..sizeof(strarr)-1], string name) +  if (!tmp[name]) +  m_delete(tmp, name); +  map += tmp; +  } +  return 1; +  } +  +  void create(string filename) +  { +  object f = Stdio.File(); +  if (!f->open(filename, "r")) +  error("Terminfo: unable to open terminfo file \"%s\"\n", filename); +  int r = load_cap(f); +  f->close(); +  if (!r) +  error("Terminfo: unparsable terminfo file \"%s\"\n", filename); +  } + } +  + class TermcapDB { +  +  MUTEX +  +  static private inherit Stdio.File; +  +  static private string buf=""; +  static private mapping(string:int|object) cache=([]); +  static private int complete_index=0; +  +  void create(string|void filename) +  { +  if (!filename) { +  string tce = getenv("TERMCAP"); +  if (tce && strlen(tce) && tce[0]=='/') +  filename = tce; +  else +  filename = "/etc/termcap"; +  } +  if (!::open(filename, "r")) +  error("failed to open termcap file %O\n", filename); +  } +  +  static private void rewind(int|void pos) +  { +  ::seek(pos); +  buf=""; +  } +  +  static private int more_data() +  { +  string q; +  q=::read(8192); +  if (q=="" || !q) return 0; +  buf+=q; +  return 1; +  } +  +  static private array(string) get_names(string cap) +  { +  sscanf(cap, "%s:", cap); +  sscanf(cap, "%s,", cap); +  return cap/"|"; +  } +  +  static private string read() +  { +  int i, st; +  string res=""; +  for (;;) +  { +  if (buf=="" && !more_data()) return 0; // eof +  +  sscanf(buf,"%*[ \t\r\n]%s",buf); +  if (buf=="") continue; +  +  if (buf[0]=='#') // comment, scan to newline +  { +  while ((i=search(buf,"\n"))<0) +  { +  // The rest of the buffer is comment, toss it and read more +  buf=""; +  if(!more_data()) return 0; // eof +  } +  buf=buf[i+1..]; +  continue; +  } +  +  break; +  } +  +  st = ::tell()-sizeof(buf); +  +  while ((i=search(buf, "\n"))<0) +  { +  if (!more_data()) return 0; // eof +  } +  +  while (buf[i-1]=='\\') +  { +  res+=buf[..i-2]; +  buf=buf[i+1..]; +  while (sscanf(buf,"%*[ \t\r]%s",buf)<2 || !sizeof(buf)) +  if (!more_data()) { +  buf = ""; +  return res; // eof, or illegal... wierd +  } +  while ((i=search(buf, "\n"))<0) +  { +  if (!more_data()) return 0; // eof +  } +  } +  +  res+=buf[..i-1]; +  buf=buf[i+1..]; +  +  foreach(get_names(res), string name) +  if(!objectp(cache[name])) +  cache[name]=st; +  +  return res; +  } +  +  static private string readat(int pos) +  { +  rewind(pos); +  return read(); +  } +  +  array(string) _indices() +  { +  LOCK; +  if(!complete_index) { +  rewind(); +  while(read()); +  complete_index = 1; +  } +  UNLOCK; +  return sort(indices(cache)); +  } +  +  array(object) _values() +  { +  array(object|int) res = ({}); +  mapping(int:string) extra = ([]); +  LOCK; +  if (complete_index) +  res = Array.map(sort(indices(cache)), +  lambda(string name, mapping(int:string) extra) { +  if (!objectp(cache[name]) && !extra[cache[name]]) +  extra[cache[name]] = readat(cache[name]); +  return cache[name]; +  }, extra); +  else { +  array(string) resi = ({}); +  string cap; +  int i = 1; +  rewind(); +  while ((cap = read())) { +  array(string) names = get_names(cap); +  object|int o = objectp(cache[names[0]]) && cache[names[0]]; +  if (!o) +  { +  o = i++; +  extra[o] = cap; +  } +  res += ({ o }) * sizeof(names); +  resi += names; +  } +  sort(resi, res); +  complete_index = 1; +  } +  UNLOCK; +  return Array.map(res, lambda(int|object x, mapping(int:object) y) { +  return objectp(x)? x : y[x]; +  }, +  mkmapping(indices(extra), +  Array.map(values(extra), +  Termcap, this_object()))); +  } +  +  static private string read_next(string find) // quick search +  { +  for (;;) +  { +  int i, j; +  +  if (buf=="" && !more_data()) return 0; // eof +  +  i=search(buf,find); +  if (i!=-1) +  { +  int j=i; +  while (j>=0 && buf[j]!='\n') j--; // find backwards +  +  if (buf!="" && buf[j+1]!='#') // skip comments +  { +  buf=buf[j+1..]; +  return read(); +  } +  +  while ((i=search(buf,"\n",j+1))<0) +  if (!more_data()) return 0; // eof +  +  buf = buf[i+1..]; +  +  continue; +  } +  for(j=-1; (i=search(buf,"\n",j+1))>=0; j=i); +  buf = buf[j+1..]; +  if (!more_data()) return 0; // eof +  } +  } +  +  object load(string term, int|void maxrecurse) +  { +  int|string|object cap; +  +  LOCK; +  if (zero_type(cache[term])) +  { +  if (!complete_index) +  { +  rewind(); +  do +  cap = read_next(term); +  while(cap && search(get_names(cap), term)<0); +  } +  } +  else if (intp(cap=cache[term])) { +  rewind(cap); +  cap = read(); +  } +  UNLOCK; +  if (stringp(cap)) +  { +  array(string) names = get_names(cap); +  if ((cap = Termcap(cap, this_object(), maxrecurse))) +  { +  LOCK; +  foreach(names, string name) +  cache[name] = cap; +  UNLOCK; +  } +  } +  return objectp(cap) && cap; +  } +  +  object `[](string name) +  { +  return load(name); +  } + } +  +  + class TerminfoDB { +  +  MUTEX +  +  static private string dir; +  static private mapping(string:object) cache = ([]); +  static private int complete_index=0; +  +  void create(string|void dirname) +  { +  if (!dirname) +  { +  foreach (({"/usr/share/lib/terminfo", "/usr/share/termcap", +  "/usr/lib/terminfo", "/usr/share/misc/terminfo"}), string dn) +  { +  array(int) s = file_stat(dn); +  if (arrayp(s) && sizeof(s)>1 && s[1]==-2) +  { +  dirname = dn; +  break; +  } +  } +  if (!dirname) { +  destruct(this_object()); +  return; +  } +  } +  +  if(sizeof(dirname)<1 || dirname[-1]!='/') +  dirname += "/"; +  +  if (!get_dir(dir = dirname)) +  error("failed to read terminfo dir %O\n", dirname); +  } +  +  array(string) _indices() +  { +  LOCK; +  if (!complete_index) { +  array(string) files; +  foreach (get_dir(dir), string a) +  if (sizeof(a) == 1) +  foreach (get_dir(dir+a), string b) +  if(zero_type(cache[b])) +  cache[b] = 0; +  complete_index = 1; +  } +  UNLOCK; +  return sort(indices(cache)); +  } +  +  array(object) _values() +  { +  return Array.map(_indices(), +  lambda(string name) { +  return cache[name] || +  Terminfo(dir+name[..0]+"/"+name); +  }); +  } +  +  object load(string term) +  { +  object ti; +  +  if (!strlen(term)) +  return 0; +  LOCK; +  if (!(ti = cache[term])) +  { +  if (file_stat(dir+term[..0]+"/"+term)) +  ti = Terminfo(dir+term[..0]+"/"+term); +  if (ti) +  cache[term] = ti; +  } +  UNLOCK; +  return ti; +  } +  +  object `[](string name) +  { +  return load(name); +  } +  + } +  + static private object defterm, deftermcap, defterminfo; +  + object defaultTermcapDB() + { +  object tcdb; +  LOCK; +  tcdb = deftermcap || (deftermcap = TermcapDB()); +  UNLOCK; +  return tcdb; + } +  + object defaultTerminfoDB() + { +  object tidb; +  LOCK; +  tidb = defterminfo || (defterminfo = TerminfoDB()); +  UNLOCK; +  return tidb; + } +  + object getTermcap(string term) + { +  object tcdb = defaultTermcapDB(); +  return tcdb && tcdb[term]; + } +  + object getTerminfo(string term) + { +  object tidb = defaultTerminfoDB(); +  return tidb && tidb[term]; + } +  + object getTerm(string|void term) + { +  if (!term) { +  object t = defterm; +  if (!t) +  { +  string tc = getenv("TERMCAP"); +  t = (tc && sizeof(tc) && tc[0]!='/'? +  Termcap(tc) : getTerm(getenv("TERM")||"dumb")); +  LOCK; +  if (!defterm) +  defterm = t; +  UNLOCK; +  } +  return t; +  } +  return getTerminfo(term) || getTermcap(term); + }   Newline at end of file added.