From c2a4065b66e4f47d02c883380a4a1fdf5b87ded3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20H=C3=BCbinette=20=28Hubbe=29?= <hubbe@hubbe.net>
Date: Thu, 6 Feb 1997 16:37:39 -0800
Subject: [PATCH] new modules (moved from being .pre.pike files)

Rev: lib/modules/array_functions.pmod:1.1
Rev: lib/modules/fifo.pmod:1.1
Rev: lib/modules/getopt.pmod:1.1
Rev: lib/modules/process.pmod:1.1
Rev: lib/modules/simulate.pmod:1.1
Rev: lib/modules/stack.pmod:1.1
Rev: lib/modules/stdio.pmod:1.1
Rev: lib/modules/string_functions.pmod:1.1
---
 lib/modules/array_functions.pmod  | 164 ++++++++++++++++++
 lib/modules/fifo.pmod             |  80 +++++++++
 lib/modules/getopt.pmod           | 276 ++++++++++++++++++++++++++++++
 lib/modules/process.pmod          |  95 ++++++++++
 lib/modules/simulate.pmod         |  74 ++++++++
 lib/modules/stack.pmod            |  30 ++++
 lib/modules/stdio.pmod            | 215 +++++++++++++++++++++++
 lib/modules/string_functions.pmod |  65 +++++++
 8 files changed, 999 insertions(+)
 create mode 100644 lib/modules/array_functions.pmod
 create mode 100644 lib/modules/fifo.pmod
 create mode 100644 lib/modules/getopt.pmod
 create mode 100644 lib/modules/process.pmod
 create mode 100644 lib/modules/simulate.pmod
 create mode 100644 lib/modules/stack.pmod
 create mode 100644 lib/modules/stdio.pmod
 create mode 100644 lib/modules/string_functions.pmod

diff --git a/lib/modules/array_functions.pmod b/lib/modules/array_functions.pmod
new file mode 100644
index 0000000000..604c6fee81
--- /dev/null
+++ b/lib/modules/array_functions.pmod
@@ -0,0 +1,164 @@
+#define error(X) throw( ({ (X), backtrace()[0..sizeof(backtrace())-2] }) )
+
+mixed map(mixed arr, mixed fun, mixed ... args)
+{
+  int e;
+  mixed *ret;
+
+  if(mappingp(arr))
+    return mkmapping(indices(arr),map(values(arr),fun,@args));
+
+  if(intp(fun))
+    return arr(@args);
+  
+  if(stringp(fun))
+    return column(arr, fun)(@args);
+
+  if(functionp(fun))
+  {
+    ret=allocate(sizeof(arr));
+    for(e=0;e<sizeof(arr);e++)
+      ret[e]=fun(arr[e],@args);
+    return ret;
+  }
+
+  error("Bad argument 2 to map_array().\n");
+}
+
+mixed filter(mixed arr, mixed fun, mixed ... args)
+{
+  int e;
+  mixed *ret;
+
+  if(mappingp(arr))
+  {
+    mixed *i, *v, r;
+    i=indices(arr);
+    ret=map(v=values(arr),fun,@args);
+    r=([]);
+    for(e=0;e<sizeof(ret);e++) if(ret[e]) r[i[e]]=v[e];
+
+    return ret;
+  }else{
+    int d;
+    ret=map(arr,fun,@args);
+    for(e=0;e<sizeof(arr);e++) if(ret[e]) ret[d++]=arr[e];
+    
+    return ret[..d-1];
+  }
+}
+
+
+int search_array(mixed *arr, mixed fun, mixed ... args)
+{
+  int e;
+
+  if(stringp(fun))
+  {
+    for(e=0;e<sizeof(arr);e++)
+      if(arr[e][fun](@args))
+	return e;
+    return -1;
+  }
+  else if(functionp(fun))
+  {
+    for(e=0;e<sizeof(arr);e++)
+      if(fun(arr[e],@args))
+	return e;
+    return -1;
+  }
+  else if(intp(fun))
+  {
+    for(e=0;e<sizeof(arr);e++)
+      if(arr[e](@args))
+	return e;
+    return -1;
+  }
+  else
+  {
+    error("Bad argument 2 to filter().\n");
+  }
+}
+
+mixed *sum_arrays(function foo, mixed * ... args)
+{
+  mixed *ret;
+  int e,d;
+  ret=allocate(sizeof(args[0]));
+  for(e=0;e<sizeof(args[0]);e++)
+    ret[e]=foo(@ column(args, e));
+  return ret;
+}
+
+varargs mixed *sort_array(array foo,function cmp, mixed ... args)
+{
+  array bar,tmp;
+  int len,start;
+  int length;
+  int foop, fooend, barp, barend;
+
+  if(!cmp || cmp==`>)
+  {
+    foo+=({});
+    sort(foo);
+    return foo;
+  }
+
+  if(cmp == `<)
+  {
+    foo+=({});
+    sort(foo);
+    return reverse(foo);
+  }
+
+  length=sizeof(foo);
+
+  foo+=({});
+  bar=allocate(length);
+
+  for(len=1;len<length;len*=2)
+  {
+    start=0;
+    while(start+len < length)
+    {
+      foop=start;
+      barp=start+len;
+      fooend=barp;
+      barend=barp+len;
+      if(barend > length) barend=length;
+      
+      while(1)
+      {
+	if(cmp(foo[foop],foo[barp],@args) <= 0)
+	{
+	  bar[start++]=foo[foop++];
+	  if(foop == fooend)
+	  {
+	    while(barp < barend) bar[start++]=foo[barp++];
+	    break;
+	  }
+	}else{
+	  bar[start++]=foo[barp++];
+	  if(barp == barend)
+	  {
+	    while(foop < fooend) bar[start++]=foo[foop++];
+	    break;
+	  }
+	}
+      }
+    }
+    while(start < length) bar[start]=foo[start++];
+
+    tmp=foo;
+    foo=bar;
+    bar=tmp;
+  }
+
+  return foo;
+}
+
+array uniq(array a)
+{
+  return indices(mkmapping(a,a));
+}
+
diff --git a/lib/modules/fifo.pmod b/lib/modules/fifo.pmod
new file mode 100644
index 0000000000..649735d6cc
--- /dev/null
+++ b/lib/modules/fifo.pmod
@@ -0,0 +1,80 @@
+
+class Fifo {
+  inherit builtin.condition : r_cond;
+  inherit builtin.condition: w_cond;
+  inherit builtin.mutex: lock;
+  
+  mixed *buffer;
+  int r_ptr, w_ptr;
+  
+  int size() {  return (w_ptr+sizeof(buffer) - r_ptr) % sizeof(buffer);  }
+  
+  mixed read()
+    {
+      mixed tmp;
+      object key=lock::lock();
+      while(!size()) r_cond::wait(key);
+      tmp=buffer[r_ptr];
+      if(++r_ptr >= sizeof(buffer)) r_ptr=0;
+      w_cond::signal();
+      return tmp;
+    }
+  
+  void write(mixed v)
+    {
+      object key=lock::lock();
+      while(size() == sizeof(buffer)) w_cond::wait(key);
+      buffer[w_ptr]=v;
+      if(++w_ptr >= sizeof(buffer)) r_ptr=0;
+      r_cond::signal();
+    }
+  
+  varargs void create(int size)
+    {
+      buffer=allocate(size || 128);
+    }
+};
+
+class Queue {
+  inherit builtin.condition: r_cond;
+  inherit builtin.mutex: lock;
+  
+  mixed *buffer=allocate(16);
+  int r_ptr, w_ptr;
+  
+  int size() {  return w_ptr - r_ptr;  }
+  
+  mixed read()
+    {
+      mixed tmp;
+      object key=lock::lock();
+      while(!size()) r_cond::wait(key);
+      tmp=buffer[r_ptr++];
+      return tmp;
+    }
+  
+  void write(mixed v)
+    {
+      object key=lock::lock();
+      if(w_ptr >= sizeof(buffer))
+      {
+	buffer=buffer[r_ptr..];
+	buffer+=allocate(sizeof(buffer)+1);
+	w_ptr-=r_ptr;
+	r_ptr=0;
+      }
+      buffer[w_ptr]=v;
+      w_ptr++;
+      r_cond::signal();
+    }
+};
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/modules/getopt.pmod b/lib/modules/getopt.pmod
new file mode 100644
index 0000000000..fdd25e98e6
--- /dev/null
+++ b/lib/modules/getopt.pmod
@@ -0,0 +1,276 @@
+//  startpid = (int)find_arg(argv, "s", ({ "start-script-pid" }),
+// 			     ({ "ROXEN_START_SCRIPT_PID"}));
+
+//  configuration_dir = find_arg(argv, "d", ({ "config-dir",
+//					     "configurations",
+//					     "configuration-directory" }),
+//			          ({ "ROXEN_CONFIGDIR", "CONFIGURATIONS" }),
+//			           "../configurations");
+
+
+string|int find_option(array argv,
+		       array|string shortform,
+		       array|string|void longform,
+		       array|string|void envvars,
+		       mixed|void def)
+{
+  mixed value;
+  int i,hasarg;
+
+  hasarg=query_num_arg() > 4;
+  if(!arrayp(longform)) longform=({longform});
+  if(!arrayp(shortform)) shortform=({shortform});
+  if(!arrayp(envvars)) envvars=({envvars});
+
+  for(i=1; i<sizeof(argv); i++)
+  {
+    if(argv[i] && strlen(argv[i]) > 1)
+    {
+      if(argv[i][0] == '-')
+      {
+	if(argv[i][1] == '-')
+	{
+	  string tmp;
+	  int nf;
+
+	  if(argv[i]=="--") break;
+
+	  sscanf(tmp=argv[i], "%s=%s", tmp, value);
+	  
+	  if(search(longform, tmp[2..]) != -1)
+	  {
+	    argv[i]=0;
+	    if(hasarg)
+	    {
+	      if(!value)
+	      {
+		if(i == sizeof(argv)-1)
+		{
+		  werror("No argument to option "+tmp+".\n");
+		  exit(1);
+		}
+		value=argv[i+1];
+		argv[i+1]=0;
+	      }
+	      return value;
+	    } else {
+	      return value || 1;
+	    }
+	  }
+	} else {
+	  int j;
+	  for(j=1;j<strlen(argv[i]);j++)
+	  {
+            string opt;
+            int pos;
+	    if(search(shortform, opt=argv[i][j..j]) != -1)
+	    {
+              string arg;
+              arg=argv[i][j+1..];
+
+	      if(hasarg)
+	      {
+		if(arg=="")
+		{
+		  if(i == sizeof(argv)-1)
+                  {
+                    werror("No argument to option -"+argv[i][j..j]+".\n");
+                    exit(1);
+                  }
+
+                  value=argv[i+1];
+		  argv[i+1] = 0;
+		} else {
+		  value=arg;
+                  arg="";
+		}
+	      } else {
+		value=1;
+	      }
+
+	      argv[i]=argv[i][..j-1]+arg;
+	      if(argv[i]=="-") argv[i]=0;
+	      return value;
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  if(arrayp(envvars))
+    foreach(envvars, value)
+      if(value && (value=getenv(value)))
+	return value;
+  
+  return def;
+}
+
+/*
+ * ({ "name", type, ({aliases}), env_var, default })
+ */
+
+constant HAS_ARG=1;
+constant NO_ARG=2;
+constant MAY_HAVE_ARG=3;
+
+#define NAME 0
+#define TYPE 1
+#define ALIASES 2
+#define ENV 3
+#define DEF 4
+
+mixed *find_all_options(string *argv, mixed *options, void|int posix_me_harder)
+{
+  mapping quick=([]);
+  foreach(options, mixed opt)
+    {
+      foreach(stringp(opt[ALIASES])?({opt[ALIASES]}):opt[ALIASES],
+      mixed optname)
+	{
+	  if(optname[0..1]=="--")
+	  {
+	    quick[optname]=opt;
+	  }else{
+	    foreach(optname[1..]/"",string optletter)
+	      {
+		quick["-"+optletter]=opt;
+	      }
+	  }
+	}
+    }
+
+  mixed *ret=({});
+  for(int e=1;e<sizeof(argv);e++)
+  {
+    if(!argv[e]) continue;
+
+    if(strlen(argv[e]) && argv[e][0]=='-')
+    {
+      if(strlen(argv[e])>1 && argv[e][1]=='-')
+      {
+	string opt=argv[e];
+	if(opt=="--") break;
+
+	string arg;
+	sscanf(opt,"%s=%s",opt, arg);
+	if(mixed *option=quick[opt])
+	{
+	  argv[e]=0;
+	  if(!arg && option[TYPE]==HAS_ARG)
+	  {
+	    if(e==sizeof(argv)-1)
+	    {
+	      werror("No argument to option "+opt+".\n");
+	      exit(1);
+	    }
+	    arg=argv[e+1];
+	    argv[e+1]=0;
+	  }
+	  ret+=({ ({ option[0], arg || 1 }) });
+	}
+      }else{
+	string *foo=argv[e]/"";
+	for(int j=1;j<strlen(foo);j++)
+	{
+	  string opt="-"+foo[j];
+	  if(mixed *option=quick[opt])
+	  {
+	    foo[j]=0;
+	    string arg;
+	    if(option[TYPE]!=NO_ARG)
+	    {
+	      arg=argv[e][j+1..];
+	      
+	      if(option[TYPE]==HAS_ARG && arg=="")
+	      {
+		if(e==sizeof(argv)-1)
+		{
+		  werror("No argument to option "+opt+".\n");
+		  exit(1);
+		}
+		arg=argv[e+1];
+		argv[e+1]=0;
+	      }else{
+		foo=foo[..j];
+	      }
+	    }
+
+	    ret+=({ ({ option[0], arg || 1 }) });
+	  }
+	}
+	argv[e]=foo*"";
+	if(argv[e]=="-") argv[e]=0;
+      }
+    }else{
+      if(posix_me_harder || getenv("POSIX_ME_HARDER"))
+	break;
+    }
+  }
+
+  multiset done=mkmultiset(column(ret, 0));
+  foreach(options, string *option)
+    {
+      if(done[ret]) continue;
+      if(sizeof(option) > 2)
+      {
+	mixed foo=option[2];
+	string name=option[0];
+	if(!foo) continue;
+	if(stringp(foo)) foo=({foo});
+	foreach(foo, foo)
+	  {
+	    if(foo=getenv(foo))
+	    {
+	      ret+=({ ({name, foo}) });
+	      done[name]=1;
+	      break;
+	    }
+	  }
+
+	if(!done && sizeof(option)>3 && option[3])
+	{
+	  ret+=({ ({name, option[3]}) });
+	  done[name]=1;
+	}
+      }
+    }
+  return ret;
+}
+
+
+string *get_args(string *argv, void|int posix_me_harder)
+{
+  int i;
+  for(i=1;i<sizeof(argv);i++)
+  {
+    if(argv[i] && strlen(argv[i])>1 && argv[i][0]=='-')
+    {
+      if(argv[i][1]=='-')
+      {
+	if(argv[i]=="--")
+	{
+	  argv[i]=0;
+	  break;
+	}else{
+	  werror("Unknown option "+argv[i]+".\n");
+	  exit(1);
+	}
+      }else{
+	if(strlen(argv[i]) == 2)
+	  werror("Unknown option "+argv[i]+".\n");
+	else
+	  werror("Unknown options "+argv[i]+".\n");
+	exit(1);
+      }
+    }else{
+      if(posix_me_harder || getenv("POSIX_ME_HARDER"))
+	break;
+    }
+  }
+
+  argv-=({0,1});
+
+  return argv;
+}
+
diff --git a/lib/modules/process.pmod b/lib/modules/process.pmod
new file mode 100644
index 0000000000..2f088ecd8a
--- /dev/null
+++ b/lib/modules/process.pmod
@@ -0,0 +1,95 @@
+#define error(X) throw( ({ (X), backtrace()[0..sizeof(backtrace())-2] }) )
+
+import stdio;
+
+static private inherit File : file;
+
+varargs int exec(string file,string ... foo)
+{
+  string path;
+  if(search(file,"/"))
+    return exece(combine_path(getcwd(),file),foo,getenv());
+
+  path=getenv("PATH");
+
+  foreach(path/":",path)
+    if(file_stat(path=combine_path(path,file)))
+      return exece(path, foo,getenv());
+
+  return 69;
+}
+
+varargs int spawn(string s,object stdin,object stdout,object stderr)
+{
+  object p;
+  int pid;
+  string t;
+
+  pid=fork();
+  
+  if(pid==-1)
+    error("No more processes.\n");
+
+  if(pid)
+  {
+    return pid;
+  }else{
+    if(stdin)
+      stdin->dup2(File("stdin"));
+
+    if(stdout)
+      stdout->dup2(File("stdout"));
+
+    if(stderr)
+      stderr->dup2(File,"stderr");
+
+    exec("/bin/sh","-c",s);
+    exit(69);
+  }
+}
+
+string popen(string s)
+{
+  object p;
+  string t;
+
+  p=file::pipe();
+  if(!p) error("Popen failed. (couldn't create pipe)\n");
+  spawn(s,0,p,0);
+  destruct(p);
+
+  t=read(0x7fffffff);
+  if(!t)
+  {
+    int e;
+    e=errno();
+    close();
+    error("Popen failed with error "+e+".\n");
+  }else{
+    close();
+  }
+  return t;
+}
+
+void system(string s)
+{
+  object p;
+  int pid;
+  string t;
+
+  p=file::pipe();
+  if(!p) error("System() failed.\n");
+  p->set_close_on_exec(0);
+  if(pid=fork())
+  {
+    destruct(p);
+    /* Nothing will ever be written here, we are just waiting for it
+     * to close
+     */
+    file::read(1);
+  }else{
+    exec("/bin/sh","-c",s);
+    exit(69);
+  }
+}
+ 
\ No newline at end of file
diff --git a/lib/modules/simulate.pmod b/lib/modules/simulate.pmod
new file mode 100644
index 0000000000..e3cb05a446
--- /dev/null
+++ b/lib/modules/simulate.pmod
@@ -0,0 +1,74 @@
+import array_functions;
+import stdio;
+import string_functions;
+
+#define error(X) throw( ({ (X), backtrace()[0..sizeof(backtrace())-2] }) )
+
+inherit regexp : regexp;
+
+varargs int member_array(mixed needle,mixed *haystack,int start)
+{
+  return search(haystack,needle,start);
+}
+
+object previous_object()
+{
+  int e;
+  mixed **trace;
+  object o,ret;
+  trace=backtrace();
+  o=function_object(trace[-2][2]);
+  for(e=sizeof(trace)-3;e>=0;e--)
+  {
+    if(!trace[e][2]) continue;
+    ret=function_object(trace[e][2]);
+    if(o!=ret) return ret;
+  }
+  return 0;
+}
+
+function this_function()
+{
+  return backtrace()[-2][2];
+}
+
+string capitalize(string s)
+{
+  return upper_case(s[0..0])+s[1..sizeof(s)];
+}
+
+function get_function(object o, string a)
+{
+  mixed ret;
+  ret=o[a];
+  return functionp(ret) ? ret : 0;
+}
+
+string *map_regexp(string *s, string reg)
+{
+  
+  regexp::create(reg);
+  s=filter(s,regexp::match);
+  regexp::create(); /* Free compiled regexp */
+  return s;
+}
+
+constant PI = 3.1415926535897932384626433832795080;
+constant all_efuns = all_constants;
+constant explode = `/;
+constant filter_array = filter;
+constant map_array = map;
+constant implode = `*;
+constant m_indices = indices;
+constant m_sizeof = sizeof;
+constant m_values = values;
+constant strstr = search;
+constant sum = `+;
+constant add_efun = add_constant;
+constant l_sizeof = sizeof;
+constant listp = multisetp;
+constant mklist = mkmultiset;
+constant aggregate_list = aggregate_multiset;
+#if efun(gethostname)
+constant query_host_name=gethostname;
+#endif
diff --git a/lib/modules/stack.pmod b/lib/modules/stack.pmod
new file mode 100644
index 0000000000..92dfb2546e
--- /dev/null
+++ b/lib/modules/stack.pmod
@@ -0,0 +1,30 @@
+#define error(X) throw( ({ (X), backtrace()[0..sizeof(backtrace())-2] }) )
+
+class stack {
+  int ptr;
+  mixed *arr=allocate(32);
+
+  void push(mixed val)
+  {
+    if(ptr==sizeof(arr)) arr+=allocate(ptr);
+    arr[ptr++]=val;
+  }
+
+  mixed pop(mixed val)
+  {
+    mixed foo;
+    if(--ptr < 0)
+      error("Stack underflow\n");
+    
+    foo=arr[ptr];
+    arr[ptr]=0; /* Don't waste references */
+    return foo;
+  }
+
+  void reset()
+  {
+    arr=allocate(32);
+    ptr=0;
+  }
+};
+
diff --git a/lib/modules/stdio.pmod b/lib/modules/stdio.pmod
new file mode 100644
index 0000000000..49ab6576d2
--- /dev/null
+++ b/lib/modules/stdio.pmod
@@ -0,0 +1,215 @@
+#include <string.h>
+import files;
+
+constant File=file;
+constant Port=port;
+
+object stderr=File("stderr");
+object stdout=File("stdout");
+
+#define error(X) throw( ({ (X), backtrace()[0..sizeof(backtrace())-2] }) )
+class FILE {
+
+#define BUFSIZE 16384
+    inherit File : file;
+
+    /* Private functions / buffers etc. */
+
+    private string b="";
+    private int bpos=0;
+
+    inline private static nomask int get_data()
+    {
+      string s;
+      b = b[bpos .. ];
+      bpos=0;
+      s = file::read(BUFSIZE,1);
+      if(!s || !strlen(s))
+	return 0;
+      b += s;
+      return 1;
+    }
+
+    inline private static nomask string extract(int bytes, int|void skip)
+    {
+      string s;
+      s=b[bpos..bpos+bytes-1];
+      bpos += bytes+skip;
+      return s;
+    }
+
+
+    /* Public functions. */
+    string gets()
+    {
+      int p;
+      while((p=search(b, "\n", bpos)) == -1)
+	if(!get_data())
+	  return 0;
+      return extract(p-bpos, 1);
+    }
+
+    int seek(int pos)
+    {
+      bpos=0;
+      b="";
+      return file::seek(pos);
+    }
+
+    int close(void|string mode)
+    {
+      bpos=0;
+      b="";
+      if(!mode) mode="rw";
+      file::close(mode);
+    }
+
+    int open(string file, void|string mode)
+    {
+      bpos=0;
+      b="";
+      if(!mode) mode="rwc";
+      return file::open(file,mode);
+    }
+
+    int open_socket()
+    {
+      bpos=0;
+      b="";
+      return file::open_socket();
+    }
+
+    object pipe()
+    {
+      bpos=0;
+      b="";
+      return file::pipe();
+    }
+
+    object dup()
+    {
+      object o;
+      o=object_program(this_object())();
+      o->assign(file::dup());
+      return o;
+    }
+
+    void set_nonblocking()
+    {
+      error("Cannot use nonblocking IO with buffered files.\n");
+    }
+
+    int printf(string fmt, mixed ... data)
+    {
+      if(!sizeof(data))
+	return file::write(fmt);
+      else
+	return file::write(sprintf(fmt,@data));
+    }
+    
+    string read(int bytes)
+    {
+      while(strlen(b) - bpos < bytes)
+	if(!get_data())
+	  break;
+
+      return extract(bytes);
+    }
+
+    string ungets(string s)
+    {
+      b=s+b[bpos..];
+      bpos=0;
+    }
+
+    int getchar()
+    {
+      if(strlen(b) - bpos < 1)
+	if(!get_data())
+	  return -1;
+
+      return b[bpos++];
+    }
+  };
+
+object stdin=FILE("stdin");
+
+private static inherit File;
+
+string read_file(string filename,void|int start,void|int len)
+{
+  object buf,f;
+  string ret,tmp;
+  f=FILE();
+  if(!f->open(filename,"r")) return 0;
+
+  switch(query_num_arg())
+  {
+  case 1:
+    ret=f->read(0x7fffffff);
+    break;
+
+  case 2:
+    len=0x7fffffff;
+  case 3:
+    while(start-- && f->gets());
+    buf=String_buffer();
+    while(len-- && (tmp=f->gets()))
+    {
+      buf->append(tmp);
+      buf->append("\n");
+    }
+    ret=buf->get_buffer();
+    destruct(buf);
+  }
+  destruct(f);
+
+  return ret;
+}
+
+string read_bytes(string filename,void|int start,void|int len)
+{
+  string ret;
+  if(!open(filename,"r"))
+    return 0;
+  
+  switch(query_num_arg())
+  {
+  case 1:
+  case 2:
+    len=0x7fffffff;
+  case 3:
+    seek(start);
+  }
+  ret=read(len);
+  close();
+  return ret;
+}
+
+int write_file(string filename,string what)
+{
+  int ret;
+  if(!open(filename,"awc"))
+    error("Couldn't open file "+filename+".\n");
+  
+  ret=write(what);
+  close();
+  return ret;
+}
+
+int file_size(string s)
+{
+  int *stat;
+  stat=file_stat(s);
+  if(!stat) return -1;
+  return stat[1]; 
+}
+
+void perror(string s)
+{
+#if efun(strerror)
+  stderr->write(s+": "+strerror(predef::errno())+"\n");
+#else
+  stderr->write(s+": errno: "+predef::errno()+"\n");
+#endif
+}
diff --git a/lib/modules/string_functions.pmod b/lib/modules/string_functions.pmod
new file mode 100644
index 0000000000..fe0389432e
--- /dev/null
+++ b/lib/modules/string_functions.pmod
@@ -0,0 +1,65 @@
+#define BEGIN 32
+
+/*
+ * Implode an array of strings to an english 'list'
+ * ie. ({"foo","bar","gazonk"}) beomces "foo, bar and gazonk"
+ */
+string implode_nicely(string *foo, string|void and)
+{
+  if(!and) and="and";
+  switch(sizeof(foo))
+  {
+  case 0: return "";
+  case 1: return foo[0];
+  default: return foo[0..sizeof(foo)-2]*", "+" "+and+" "+foo[-1];
+  }
+}
+
+string strmult(string str, int num)
+{
+#if 1
+  num*=strlen(str);
+  while(strlen(str) < num) str+=str;
+  return str[0..num-1];
+#endif
+#if 0
+  return sprintf("%~n",str,strlen(str)*num);
+#endif
+}
+
+class String_buffer {
+  string *buffer=allocate(BEGIN);
+  int ptr=0;
+  
+  static void fix()
+    {
+      string tmp=buffer*"";
+      buffer=allocate(strlen(tmp)/128+BEGIN);
+      buffer[0]=tmp;
+      ptr=1;
+    }
+  
+  string get_buffer()
+    {
+      if(ptr != 1) fix();
+      return buffer[0];
+    }
+  
+  void append(string s)
+    {
+      if(ptr==sizeof(buffer)) fix();
+      buffer[ptr++]=s;
+    }
+  
+  mixed cast(string to)
+    {
+      if(to=="string") return get_buffer();
+      return 0;
+    }
+  
+  void flush()
+    {
+      buffer=allocate(BEGIN);
+      ptr=0;
+    }
+};
-- 
GitLab