#line 227 "/home/travis/build/felix-lang/felix/src/packages/flx_pkgconfig.fdoc"
  class FlxPkgConfig_core
  {
    open Lexer;
  
    gen flx_pkgconfig (args:list[string]) : int * list[string] =
    {
      proc print_help {
        println$ "flx_pkgconfig [options] pkg pkg ...";
        println$ "  returns code 1 if any packages are missing unless --noerror is specified";
        println$ "  prints package or field list to standard output on one line";
        println$ "options: (follows GNU conventions)";
        println$ "  --path=dirname        set database directory name";
        println$ "  --path+=dirname       append database directory name";
        println$ "  --extension=fpc       set resource descriptor extensions,";
        println$ "                          default 'fpc' use 'pc' for pkgconfig databases";
        println$ "  -h";
        println$ "  --hide                only process first package in path with a given name";
        println$ "                          default, process all occurences";
        println$ "  --list                list available packages from specified set";
        println$ "  --missing             list missing packages from specified set";
        println$ "  --noerror             do not return 1 because of missing packages";
        println$ "  -r";
        println$ "  --rec                 form transitive closure of specified set based on Requires field";
        println$ "  --rec=field           form transitive closure of specified set based on specified field";
        println$ "  -b";
        println$ "  --backwards           process specified packages in reverse order";
        println$ "  --field=field         collate values of field in package set";
        println$ "  --keepleftmost        remove duplicate values in output keeping only leftmost occurrence";
        println$ "  --keeprightmost       remove duplicate values in output keeping only rightmost occurrence";
        println$ "  --keepall             keep duplicate values in output";
        println$ "  @filename             Replace with arguments from filename, one line per argument";
      }
  
      proc pre_incr:&lex_iterator = "++*$1;";
  
      fun lexit(ini:lex_iterator, finish:lex_iterator): lex_iterator * string =
      {
        //println$ "lexit input='" + string_between(ini,finish)+"'";
  
        var start = ini;
  
        // already at end
        if start == finish do
          return start, "";
  
        // eat white space
        elif *start == char(' ') do
          ++start;
          while start != finish and *start == char(' ') do ++start; done;
          return start,"";
  
        // double quoted string
        elif *start == char('"') do
          ++start;
          p1 := start;
          while start != finish and *start != char('"') do ++start; done;
          if start == finish do
            return start,string_between(p1,start);
          else
            return start+1,string_between(p1, start);
          done;
  
        // single quoted string
        elif *start == char("'") do
          ++start;
          p2 := start;
          while start != finish and *start != char("'") do ++start; done;
          if start == finish do
            return start,string_between(p2,start);
          else
            return start+1,string_between(p2, start);
          done;
  
        done;
        // identifier
        p3 := start;
        while start != finish and *start != char(" ")  do ++start; done;
        return start,string_between(p3,start);
      }
  
      fun lexstr(s':string): list[string] =
      {
        var s = s';
        val first = start_iterator s;
        val finish = end_iterator s;
        var current = first;
        var words = Empty[string];
        while current != finish do
          match lexit(current,finish) with
          | next,lexeme =>
            {
              current = next;
              if lexeme != "" do words = Cons(lexeme,words); done;
            }
          endmatch;
        done
        //println$ "Words='" + str(rev words)+"'";
        return rev words;
      }
  
      macro val streq = eq of (string * string);
  
      var path=Env::getenv("PKG_CONFIG_PATH");
  
      // parse arguments
      var fields = Empty[string];
      var pkgs = Empty[string];
  
      var hide = false; // only find first file in path
      var require_pkg_exists = true; // fail if file not found
      var missing = false; // report missing packages
      var require_field_exists = false; // fail if file doesn't contain field
      var recfields = Empty[string];
      var dolist = false;
      var listkeys = false;
      var return_code = 0;
      var backwards = false;
      enum keep_t {keepall, keepleftmost, keeprightmost};
      var keep= keepleftmost;
      var extension = "fpc";
  
      fun is_prefix_of(p:string,w:string)=> p == w.[to len p];
  
      fun xfind(flags: string, c: string) =>
       match find(flags, c) with
       | #None => false
       | Some _ => true
       endmatch
      ;
  
      proc parse_args(args:list[string])
      {
        match args with
        | #Empty => {}
        | Cons (arg,tail) =>
          {
            fun prefix(x:string)=>is_prefix_of(x,arg);
  
            if prefix("--hide") do hide = true;
            elif prefix("--backwards") do backwards = true;
            elif prefix("--list") do dolist = true;
            elif prefix("--missing") do missing = true;
            elif prefix("--noerror") do require_pkg_exists = false;
            elif prefix("--keeprightmost") do keep = keeprightmost;
            elif prefix("--keepleftmost") do keep = keepleftmost;
            elif prefix("--keepall") do keep = keepall;
  
            elif "--field" == arg.[0 to 7] do
              fields = fields + arg.[8 to];
  
            elif "--extension" == arg.[0 to 11] do
              extension = arg.[12 to];
  
            elif "-" == arg.[0 to 1] and "-" != arg.[1 to 2] do
              flags := arg.[1 to];
              if xfind(flags, "r") do
                recfields = append_unique streq recfields "Requires";
              done;
  
              if xfind(flags,"h") do hide = true; done;
              if xfind(flags,"b") do backwards = true; done;
              if xfind(flags,"l") do dolist = true; done;
  
            elif "--rec" == arg.[0 to 5] do
              var fld = arg.[6 to];
              fld = if fld == "" then "Requires" else fld endif;
              recfields = append_unique streq recfields fld;
  
            // add to path
            elif "--path+" == arg.[0 to 7] do
              val x = arg.[8 to];
              if path != "" do
                path= path + ":" + x;
              else
                path= x;
              done;
  
            // set path
            elif "--path" == arg.[0 to 6] do
              path= arg.[7 to];
  
            elif "--help" == arg do
              print_help;
              System::exit(0);
  
            elif "@" == arg.[0 to 1] do
              val data = load$ strip arg.[1 to];
              parse_args$ split(data,c" \n\r\t,");
  
            // ignore unknown options
            elif "-" == arg.[0 to 1] do ;
  
            // ignore empty arguments
            elif "" == arg do ;
  
            // package name
            else
              pkgs = pkgs + arg;
            done;
            parse_args(tail);
          }
        endmatch;
      }
  
      parse_args(args);
  
      //print$ "Fields   = " + str fields; endl;
      //print$ "Packages = " + str pkgs; endl;
  
      fun reattach_drive_letters : list[string] -> list[string] =
        | Cons (a, Cons (b, tail)) =>
            if (len(a) == size 1 and isalpha(a.[0]) and b.startswith('\\')) then
              Cons (a+':'+b, reattach_drive_letters tail)
            else
              Cons (a, reattach_drive_letters (Cons (b, tail)))
            endif
        | other => other // 1 or 0 elements left
      ;
  
      val dirs=reattach_drive_letters(split(path, char ':'));
  
      // print$ "Path = " + str dirs; endl;
  
      var result = Empty[string];
  
      fun check_id (s:string) = {
        var acc=true;
        for elt in s do acc = acc and isalphanum elt; done
        return acc;
      }
  
      fun get_field(line:string):string * string =>
          match find (line,char ':') with
          | #None => "",""
          | Some n =>
              strip line.[to n],
              strip line.[n+1 to]
          endmatch
        ;
  
  
      fun get_variable(line:string):string * string =>
          match find (line,char '=') with
          | #None => "",""
          | Some n =>
              let name = strip line.[to n] in
              let value = strip line.[n+1 to] in
              if check_id name then name,value else "",""
          endmatch
        ;
  
      proc add_val(v:string){
       result = insert_unique streq result v;
      //  result = rev$ Cons(v, rev result);
      }
  
      proc tail_val(v:string){
         result = append_unique streq result v;
      //  result = Cons(v, result);
      }
  
      proc keep_val (v:string){
        result = result + v;
      }
  
      proc handle_pkg (pkg:string, trace:list[string]){
  //eprintln$ "Handle_pkg pkg= " + pkg + " trace= " + trace.str;
         var variables = Empty[string * string];
  
         if mem streq trace pkg return;
         var found = false;
         iter(proc (dir:string){
           val filename =
             if dir=="" then "." else dir endif + #Filename::sep + pkg + "."+extension
           ;
           //print filename; endl;
  
           // examine line of one file
           file := fopen_input filename;
           if valid file do
             if dolist do
               match keep with
               | #keepleftmost => add_val pkg;
               | #keeprightmost => tail_val pkg;
               | #keepall => keep_val pkg;
               endmatch;
             done
             var lines = Empty[string];
             var line = readln file;
             while line != "" do
               line = line.strip;
               if line != "" and line.[0] != char "#" do
                 lines = Cons(line,lines);
               done
               line = readln file;
             done
             if not backwards do lines = rev lines; done;
  
             iter (proc (line:string)
             {
               //print line;
               def var variable, var vval = get_variable(line);
               if variable != "" do
                 var bdy = search_and_replace variables vval;
                 variables = Cons ( ("${"+variable+"}",bdy), variables);
               else
                 def var key, var value = get_field(line);
                 if listkeys call add_val key;
                 var values = lexstr(value);
                 values = map (search_and_replace variables) values;
                 if mem streq fields key do
                   match keep with
                   | #keepleftmost => { iter add_val values; }
                   | #keeprightmost => { iter tail_val values; }
                   | #keepall => { iter keep_val values; }
                   endmatch;
                 done;
  //eprintln$ "Chase dependent packages key = " + key + " recfields = " + recfields.str;
                 // chase dependent packages
                 if mem streq recfields key do
  //eprintln$ "FOUND";
                   iter (proc (s:string){
                     handle_pkg$ s,Cons(pkg,trace);
                   })
                   values;
                 done
  //eprintln$ "DONE  dependent packages key = " + key + " recfields = " + recfields.str;
  
               done
             })
             lines
             ;
             fclose file;
             found = true;
             if hide return; // only find first file in path
           done;
         })
         dirs;
         if not found do
           eprintln$ "package not found: " + pkg;
           if require_pkg_exists do return_code = 1; done;
           if missing call add_val(pkg);
         done;
      }
  
      var original_pkgs = pkgs;
  //eprintln$ "+++++++++++++++++++++++++";
  //eprintln$ "TOP LEVEL HANDLING PACKAGES " + original_pkgs.str;
      while not is_empty pkgs do
        match pkgs with
        | #Empty => {}
        | Cons (pkg,tail) =>
          {
  //eprintln$ "TOP LEVEL HANDLE ONE PACKAGE " + pkg.str;
            pkgs = tail;
            handle_pkg(pkg,Empty[string]);
  //eprintln$ "DONE: TOP LEVEL HANDLE ONE PACKAGE " + pkg.str;
          }
        endmatch;
      done;
  //eprintln$ "DONE: TOP LEVEL HANDLING PACKAGES " + original_pkgs.str;
  //eprintln$ " ************************";
  
      return return_code, result;
    }
  }