#line 1831 "/home/travis/build/felix-lang/felix/src/packages/web.fdoc"
  publish """
  Simple config file reader. Splits key value pairs seperated by the equals character.
  Skips lines where first non-space character is the # character. Max configuration file size
  is 65535 bytes
  
  Example input:
    # Sample configuration file
    delay         =    0.05
    port          =    1234
    document_root =  ./html
  
  Example code:
    open SimpleConfig;
    if System::argc > 0 do
      var arg = System::argv 1;
      println$ "config file:" + arg;
      iter (proc (kv:string*string) { println(kv.(0)+":"+kv.(1)); })
           (read_config(System::argv 1));
    else
      println("No config file specified");
    done
  """
  
  class SimpleConfig {
    requires header '#include <sys/stat.h>';
    open Assoc_list;
    open Csv;
  
    typedef configuration = assoc_list[string,string];
  
    publish """
    Reads configuration file and returns associative list
    """
    fun read_config(config_file:string):configuration = {
      val fsz =  fsize(config_file);
      var config = Empty[string^2];
      if fsz > size(0) and fsz < size(65535) do
        val handle = fopen_input config_file;
        if valid handle do
          val config_text = load(handle);
          fclose(handle);
          println$ "Loaded config file " + config_file;
          config = config + read_config_text(config_text);
        done
      done
      return config;
    }
  
    fun read_config_text(config_text:string):configuration ={
      print$ "[Config Data]\n" + config_text+"[End Config Data]\n";
      var config = Cons(('INSTALL_ROOT',#Config::std_config.FLX_SHARE_DIR.[to -6]),
                        Empty[string^2]);
      iter (proc (line:string) {config = config + parse(line);})
               (split(str(config_text),"\n"));
      return apply_param_vars(config);
    }
  
  
    publish """
      returns opt param value for given key
    """
    fun get_param(params:list[string*string],name:string) =>
       find (fun (a:string,b:string) => eq(a,b)) params name;
  
    publish """
      return list strings from comma seperated parameter value
    """
    fun get_param_list(params:list[string*string],name:string) =>
      match get_param(params,name) with |Some v => get_csv_values(v) |_ => Empty[string] endmatch;
  
    publish """
       Supports $variables in config files. Uses previously defined paramater keys
       as $ variables. Only supports first occurance of $variable. Also
       $INSTALL_ROOT is available nad populated with the value for the felix
       install root
    """
    fun apply_param_vars (par:list[string*string]):list[string*string] ={
      var kp:string = ""; var vp:string = "";
      return map (fun (k:string,v:string) = {
        kp = k; vp = v;
        iter (proc (k1:string,v1:string) {
          kp,vp = match find(vp,k1) with
            |Some p => (kp, substring(vp,0,(p - 1)) + v1 +
                            substring(vp,p+int(k1.len),vp.len))
            |_ => (kp,vp)
          endmatch;
        }) par;
        return (kp,vp);
      }) par;
    }
  
    fun apply_param_vars_to (par:list[string*string],v:string):string ={
      var vp:string;
      vp = v;
      iter (proc (k1:string,v1:string) {
        vp = match find(vp,k1) with
            |Some p => substring(vp,0,(p - 1)) + v1 +
                        substring(vp,p+int(k1.len),vp.len)
            |_ => vp
          endmatch;
        }) par;
        return vp;
    }
  
    fun apply_param_vars_to (par:list[string*string],l:list[string]):list[string] =>
      (map (fun (s:string) => apply_param_vars_to (par,s)) (l));
  
    private fun parse(line:string):list[string*string] =>
      if startswith (strip line) (char '#') then
        Empty[string*string]
      else
        match split_first(line, "=") with
          |Some s => list[string*string]((strip(s.(0)),strip(s.(1))))
          |None => Empty[string*string]
        endmatch
      endif;
  
    private fun split_first (x:string, c:string): opt[string*string] ={
      return match find_first_of (x, c) with
        | #None => None[string*string]
        | Some n => Some(strip(x.[to n]),strip(x.[n+1 to]))
        endmatch
      ;
    }
  
    private proc fsize_: string*&size = """
      {struct stat st;
       stat($1.c_str(), &st);
       *$2 = st.st_size;}
    """;
  
    private gen fsize(name:string):size = {
      var sz:size;
      fsize_(name,&sz);
      return sz;
    }
  }