- examples are also added as XML files

- examples are also added as XML files

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
:)
import module namespace file = "http://expath.org/ns/file";

import module namespace xqdoc       = "http://www.zorba-xquery.com/modules/xqdoc";
import module namespace batch       = "http://www.zorba-xquery.com/modules/xqdoc/batch";
import module namespace fetch = "http://www.zorba-xquery.com/modules/fetch";
import module namespace xqdoc-html  = "http://www.zorba-xquery.com/xqdoc-html" at "xqdoc-html.xqy";

declare namespace xqd = "http://www.xqdoc.org/1.0";
declare namespace z   = "http://zorba.io/manifest";
declare namespace xs  = "http://www.w3.org/2001/XMLSchema";

declare variable $zorbaManifestPath   as xs:string external;
declare variable $xhtmlRequisitesPath as xs:string external;
declare variable $xqdocBuildPath      as xs:string external;
declare variable $zorbaVersion        as xs:string external;

declare variable $slash := file:directory-separator();
declare variable $serXML:= <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                             <output:method   value="xml" />
                             <output:encoding value="UTF-8"/>
                           </output:serialization-parameters>;

declare %private function local:write-Doxygen(
  $content,
  $name as xs:string,
  $uri as xs:string?,
  $type as xs:string){
fn:serialize(<doxygen><compounddef id="{$name}-{$type}" kind="{$type}">
               <detaileddescription><para><h3>{concat("Content of ", $name, " " , $type)}</h3><programlisting type="{$type}">{fn:serialize($content,$serXML)}</programlisting></para></detaileddescription>
             </compounddef></doxygen>,$serXML)
};

(: Delete previous version of the documentation :)
if(file:exists($xqdocBuildPath))
then file:delete($xqdocBuildPath);
else ();

(: Copy Schemas :)
file:create-directory($xqdocBuildPath || $slash ||  "schemas");
file:create-directory($xqdocBuildPath || $slash || "xml");
let $base := $xhtmlRequisitesPath || $slash || ".." || $slash || ".." || $slash || ".." || $slash
let $dest := $xqdocBuildPath || $slash || "xml"
for $schema in file:list($base, true(), "*.xsd")
where not(ends-with($schema, ".ent.xsd")) and not(ends-with($schema, ".dtd.xsd"))
let $schema-doc := doc($base || $slash || trace($schema, "schema"))
let $target-uri := $schema-doc/xs:schema/@targetNamespace/string()
let $name := replace(replace($target-uri, "http://", ""), "/", "_")
return {
  (file:write-text($xqdocBuildPath || $slash || "schemas" || $slash || $name || ".xsd", $schema-doc),
  file:write-text($dest || $slash || replace(replace($target-uri, "http://", ""), "/", "_") || "-schema.xml",
                  local:write-Doxygen($schema-doc, $name, $target-uri, "schema"))
  )
};

file:create-directory($xqdocBuildPath || $slash ||  "examples");
variable $manifest := parse-xml(file:read-text($zorbaManifestPath));
for $module in $manifest/z:manifest/z:module
let $uri := $module/z:uri/text()
let $xqdoc := xqdoc:xqdoc($uri)
let $module-name := tokenize($uri,"/")[last()]
return {
  (: Copy Examples :)
  for $example in $xqdoc//xqd:custom[@tag="example"]/text()
  let $source := $module/z:projectRoot/text() || $slash || $example
  let $test-name := trace(tokenize($source,"/")[last()], "example-name")
  let $destination := $xqdocBuildPath || $slash ||  "examples" || $slash || concat($module-name, "_", $test-name)
  let $dest := $xqdocBuildPath || $slash ||  "xml" || $slash || concat($module-name, "_", substring-before($test-name,".xq")) || "-example.xml"
  return {
    file:copy($source, $destination);
    file:write-text($dest,
                   local:write-Doxygen(file:read-text($source),$test-name, $uri, "example"));
  }
}

(: XQDoc Batch :)
let $static-folders := ("js", "css", "images")
let $static-folders := for $static in $static-folders return $xhtmlRequisitesPath || $slash || $static
let $template := parse-xml(file:read-text($xhtmlRequisitesPath || $slash || "template.xml"))/*
let $output-folder := $xqdocBuildPath
let $modules := xqdoc-html:modules($manifest)
return batch:build-xqdoc($output-folder, $static-folders, $template, $modules);

(: Copy modules :)
for $module in $manifest/z:manifest/z:module
let $uri := $module/z:uri/text()
let $module := fetch:content($uri, "MODULE")
return
  file:write-text($xqdocBuildPath || $slash || "xml" || $slash || replace(substring-after($uri, "http://"),"/","_") || "-module.xml",
                  local:write-Doxygen($module, tokenize($uri,"/")[last()], $uri, "module"));

trace("","XQDoc generated successfully!");

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
:)
import module namespace file = "http://expath.org/ns/file";

import module namespace xqdoc       = "http://www.zorba-xquery.com/modules/xqdoc";
import module namespace batch       = "http://www.zorba-xquery.com/modules/xqdoc/batch";
import module namespace fetch = "http://www.zorba-xquery.com/modules/fetch";
import module namespace xqdoc-html  = "http://www.zorba-xquery.com/xqdoc-html" at "xqdoc-html.xqy";

declare namespace xqd = "http://www.xqdoc.org/1.0";
declare namespace z   = "http://zorba.io/manifest";
declare namespace xs  = "http://www.w3.org/2001/XMLSchema";

declare variable $zorbaManifestPath   as xs:string external;
declare variable $xhtmlRequisitesPath as xs:string external;
declare variable $xqdocBuildPath      as xs:string external;
declare variable $zorbaVersion        as xs:string external;

declare variable $slash := file:directory-separator();
declare variable $serXML:= <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                             <output:method   value="xml" />
                             <output:encoding value="UTF-8"/>
                           </output:serialization-parameters>;

declare %private function local:write-Doxygen(
  $content,
  $name as xs:string,
  $uri as xs:string?,
  $type as xs:string){
fn:serialize(<doxygen><compounddef id="{$name}-{$type}" kind="{$type}">
               <detaileddescription><para><h3>{concat("Content of ", $uri, " " , $type)}</h3><programlisting type="{$type}">{fn:serialize($content,$serXML)}</programlisting></para></detaileddescription>
             </compounddef></doxygen>,$serXML)
};

(: Delete previous version of the documentation :)
if(file:exists($xqdocBuildPath))
then file:delete($xqdocBuildPath);
else ();

(: Copy Schemas :)
file:create-directory($xqdocBuildPath || $slash ||  "schemas");
file:create-directory($xqdocBuildPath || $slash || "xml");
let $base := $xhtmlRequisitesPath || $slash || ".." || $slash || ".." || $slash || ".." || $slash
let $dest := $xqdocBuildPath || $slash || "xml"
for $schema in file:list($base, true(), "*.xsd")
where not(ends-with($schema, ".ent.xsd")) and not(ends-with($schema, ".dtd.xsd"))
let $schema-doc := doc($base || $slash || trace($schema, "schema"))
let $target-uri := $schema-doc/xs:schema/@targetNamespace/string()
let $name := replace(replace($target-uri, "http://", ""), "/", "_")
return {
  (file:write-text($xqdocBuildPath || $slash || "schemas" || $slash || $name || ".xsd", $schema-doc),
  file:write-text($dest || $slash || replace(replace($target-uri, "http://", ""), "/", "_") || "-schema.xml",
                  local:write-Doxygen($schema-doc, $name, $target-uri, "schema"))
  )
};

file:create-directory($xqdocBuildPath || $slash ||  "examples");
variable $manifest := parse-xml(file:read-text($zorbaManifestPath));
for $module in $manifest/z:manifest/z:module
let $uri := $module/z:uri/text()
let $xqdoc := xqdoc:xqdoc($uri)
let $module-name := tokenize($uri,"/")[last()]
return {
  (: Copy Examples :)
  for $example in $xqdoc//xqd:custom[@tag="example"]/text()
  let $source := $module/z:projectRoot/text() || $slash || $example
  let $test-name := trace(tokenize($source,"/")[last()], "example-name")
  let $destination := $xqdocBuildPath || $slash ||  "examples" || $slash || concat($module-name, "_", $test-name)
  let $dest := $xqdocBuildPath || $slash ||  "xml" || $slash || concat($module-name, "_", substring-before($test-name,".xq")) || "-example.xml"
  return {
    file:copy($source, $destination);
    (:
    file:write-text($dest,
                   local:write-Doxygen(file:read-text($source),substring-before($destination,".xq"), "example")); :)
  }
}

(: XQDoc Batch :)
let $static-folders := ("js", "css", "images")
let $static-folders := for $static in $static-folders return $xhtmlRequisitesPath || $slash || $static
let $template := parse-xml(file:read-text($xhtmlRequisitesPath || $slash || "template.xml"))/*
let $output-folder := $xqdocBuildPath
let $modules := xqdoc-html:modules($manifest)
return batch:build-xqdoc($output-folder, $static-folders, $template, $modules);

(: Copy modules :)
for $module in $manifest/z:manifest/z:module
let $uri := $module/z:uri/text()
let $module := fetch:content($uri, "MODULE")
return
  file:write-text($xqdocBuildPath || $slash || "xml" || $slash || replace(substring-after($uri, "http://"),"/","_") || "-module.xml",
                  local:write-Doxygen($module, tokenize($uri,"/")[last()], $uri, "module"));

trace("","XQDoc generated successfully!");


[FIX] DBA Fixes

[FIX] DBA Fixes

(:~
 : Logging page.
 :
 : @author Christian Grün, BaseX Team, 2014-16
 :)
module namespace dba = 'dba/logs';

import module namespace cons = 'dba/cons' at '../modules/cons.xqm';
import module namespace html = 'dba/html' at '../modules/html.xqm';
import module namespace tmpl = 'dba/tmpl' at '../modules/tmpl.xqm';
import module namespace util = 'dba/util' at '../modules/util.xqm';

(:~ Top category :)
declare variable $dba:CAT := 'logs';

(:~
 : Logging page.
 : @param  $sort     table sort key
 : @param  $name     name (date) of log file
 : @param  $loglist  search term for log list
 : @param  $logs     search term for log entries
 : @param  $error    error string
 : @param  $info     info string
 : @param  $page     current page
 : @return page
 :)
declare
  %rest:GET
  %rest:path("/dba/logs")
  %rest:query-param("sort",    "{$sort}", "")
  %rest:query-param("name",    "{$name}")
  %rest:query-param("loglist", "{$loglist}")
  %rest:query-param("logs",    "{$logs}")
  %rest:query-param("error",   "{$error}")
  %rest:query-param("info",    "{$info}")
  %rest:query-param("page",    "{$page}", 1)
  %output:method("html")
function dba:logs(
  $sort     as xs:string,
  $name     as xs:string?,
  $loglist  as xs:string?,
  $logs     as xs:string?,
  $error    as xs:string?,
  $info     as xs:string?,
  $page     as xs:integer
) as element(html) {
  cons:check(),

  let $loglists := dba:loglist($sort, $loglist)
  let $error := if($loglists) then $error else $cons:DATA-ERROR
  return tmpl:wrap(map { 'top': $dba:CAT, 'info': $info, 'error': $error },

        <form action="javascript:void(0);">

{
            if($name) then html:link('Logs:', $dba:CAT) else 'Logs:', ' ',
            <input size="14" name="loglist" id="loglist" value="{ $loglist }"
                   placeholder="regular expression" onkeyup="logList();"/>
          }

        </form>
        <form action="{ $dba:CAT }" method="post" class="update" autocomplete="off">
          <input type='hidden' name='name' id='name' value='{ $name }'/>
          <input type='hidden' name='sort' id='sort' value='{ $sort }'/>
          <input type='hidden' name='page' id='page' value='{ $page }'/>

{ $loglists }

        </form>

      {
        if($name) then (
          <form action="download-logs" method="post" id="resources">

              { $name }:
              <input type="hidden" name="name" value="{ $name }"/>
              <input type="hidden" name="loglist" value="{ $loglist }"/>
              <input size="40" id="logs" name="logs" value="{ head(($loglist, $logs)) }"
                placeholder="regular expression"
                onkeyup="logEntries();"/>
              { html:button('download', 'Download') }

          </form>,

,

        ) else (),
        html:focus(if($name) then 'logs' else 'loglist')
      }

  )
};

(:~
 : Returns entries of a specific log file.
 : @param  $query    search term
 : @param  $names    name of selected log files
 : @param  $sort     table sort key
 : @param  $loglist  search term for log list
 : @param  $page     current page
 : @return html elements
 :)
declare
  %rest:POST("{$query}")
  %rest:path("/dba/log")
  %rest:query-param("name",    "{$name}")
  %rest:query-param("loglist", "{$loglist}")
  %rest:query-param("sort",    "{$sort}", "")
  %rest:query-param("page",    "{$page}", 1)
  %output:method("html")
  %output:indent("no")
  %rest:single
function dba:log(
  $name     as xs:string,
  $query    as xs:string?,
  $loglist  as xs:string?,
  $sort     as xs:string,
  $page     as xs:integer
) as element()* {
  cons:check(),

  let $logs := try {
    util:eval("admin:logs($name, true())[matches(., $query, 'i')]",
      map { 'name': $name, 'query': $query }
    )
  } catch * { () }

  where $logs
  let $rows :=
    for $log in $logs
    return <row time='{ $log/@time }' address='{ $log/@address }' user='{ $log/@user}'
                type='{ $log/@type }' ms='{ $log/@ms }' message='{ $log }'/>
  let $headers := (
    <time type='time' order='desc'>Time</time>,
    <address>Address</address>,
    <user>User</user>,
    <type>Type</type>,
    <ms type='decimal' order='desc'>ms</ms>,
    <message>Message</message>
  )
  return html:table($headers, $rows, (),
    map { 'name': $name, 'loglist': $loglist, 'logs': $query },
    map { 'sort': head(($sort[.], 'time')), 'page': $page }
  )
};

(:~
 : Returns log files matching the specified query.
 : @param  $sort   table sort key
 : @param  $query  search term
 : @return html elements
 :)
declare
  %rest:POST("{$query}")
  %rest:path("/dba/loglist")
  %rest:query-param("sort", "{$sort}")
  %output:method("html")
  %rest:single
function dba:loglist(
  $sort   as xs:string?,
  $query  as xs:string?
) as element()* {
  cons:check(),

  let $logs := try {
    util:eval("for $log in admin:logs()
      let $date := $log/(@date,text())/string()
      where not($query) or (some $entry in admin:logs($date) satisfies matches($entry, $query, 'i'))
      order by $date descending
      return $log", map { 'query': $query }
    )
  } catch * { () }
  where $logs
  let $rows :=
    (: legacy (7.8.1): $a/@date :)
    for $a in $logs
    order by $a descending
    (: legacy (7.8.1): $a/@date :)
    return <row name='{ ($a/(@date,text())) }' size='{ $a/@size }'/>
  let $headers := (
    <name>Name</name>,
    <size type='bytes'>Size</size>
  )
  let $buttons := html:button('delete-logs', 'Delete', true())
  let $link := function($value) { $dba:CAT }
  return html:table($headers, $rows, $buttons, map { 'sort': $sort, 'loglist': $query },
    map { 'link': $link })
};

(:~
 : Redirects to the specified action.
 : @param  $action  action to perform
 : @param  $names   names of selected log files
 :)
declare
  %rest:POST
  %rest:path("/dba/logs")
  %rest:query-param("action", "{$action}")
  %rest:query-param("name",   "{$names}")
  %output:method("html")
function dba:action(
  $action  as xs:string,
  $names   as xs:string*
) {
  web:redirect($action, map { 'name': $names[.], 'redirect': $dba:CAT })
};

(:~
 : Logging page.
 :
 : @author Christian Grün, BaseX Team, 2014-16
 :)
module namespace dba = 'dba/logs';

import module namespace cons = 'dba/cons' at '../modules/cons.xqm';
import module namespace html = 'dba/html' at '../modules/html.xqm';
import module namespace tmpl = 'dba/tmpl' at '../modules/tmpl.xqm';
import module namespace util = 'dba/util' at '../modules/util.xqm';

(:~ Top category :)
declare variable $dba:CAT := 'logs';

(:~
 : Logging page.
 : @param  $sort     table sort key
 : @param  $name     name (date) of log file
 : @param  $loglist  search term for log list
 : @param  $logs     search term for log entries
 : @param  $error    error string
 : @param  $info     info string
 : @param  $page     current page
 : @return page
 :)
declare
  %rest:GET
  %rest:path("/dba/logs")
  %rest:query-param("sort",    "{$sort}", "")
  %rest:query-param("name",    "{$name}")
  %rest:query-param("loglist", "{$loglist}")
  %rest:query-param("logs",    "{$logs}")
  %rest:query-param("error",   "{$error}")
  %rest:query-param("info",    "{$info}")
  %rest:query-param("page",    "{$page}", 1)
  %output:method("html")
function dba:logs(
  $sort     as xs:string,
  $name     as xs:string?,
  $loglist  as xs:string?,
  $logs     as xs:string?,
  $error    as xs:string?,
  $info     as xs:string?,
  $page     as xs:integer
) as element(html) {
  cons:check(),

  let $loglists := dba:loglist($sort, $loglist)
  let $error := if($loglists) then $error else $cons:DATA-ERROR
  return tmpl:wrap(map { 'top': $dba:CAT, 'info': $info, 'error': $error },

        <form action="javascript:void(0);">

{
            if($name) then html:link('Logs:', $dba:CAT) else 'Logs:', ' ',
            <input size="14" name="loglist" id="loglist" value="{ $loglist }"
                   placeholder="regular expression" onkeyup="logList();"/>
          }

        </form>
        <form action="{ $dba:CAT }" method="post" class="update" autocomplete="off">
          <input type='hidden' name='name' id='name' value='{ $name }'/>
          <input type='hidden' name='sort' id='sort' value='{ $sort }'/>
          <input type='hidden' name='page' id='page' value='{ $page }'/>

{ $loglists }

        </form>

      {
        if($name) then (
          <form action="download-logs" method="post" id="resources">

              { $name }:
              <input type="hidden" name="name" value="{ $name }"/>
              <input type="hidden" name="loglist" value="{ $loglist }"/>
              <input size="40" id="logs" name="logs" value="{ head(($loglist, $logs)) }"
                placeholder="regular expression"
                onkeyup="logEntries();"/>
              { html:button('download', 'Download') }

          </form>,

,

        ) else (),
        html:focus(if($name) then 'logs' else 'loglist')
      }

  )
};

(:~
 : Returns entries of a specific log file.
 : @param  $query    search term
 : @param  $names    name of selected log files
 : @param  $sort     table sort key
 : @param  $loglist  search term for log list
 : @param  $page     current page
 : @return html elements
 :)
declare
  %rest:POST("{$query}")
  %rest:path("/dba/log")
  %rest:query-param("name",    "{$name}")
  %rest:query-param("loglist", "{$loglist}")
  %rest:query-param("sort",    "{$sort}", "time")
  %rest:query-param("page",    "{$page}", 1)
  %output:method("html")
  %output:indent("no")
  %rest:single
function dba:log(
  $name     as xs:string,
  $query    as xs:string?,
  $loglist  as xs:string?,
  $sort     as xs:string,
  $page     as xs:integer
) as element()* {
  cons:check(),

  let $logs := try {
    util:eval("admin:logs($name, true())[matches(., $query, 'i')]",
      map { 'name': $name, 'query': $query }
    )
  } catch * { () }

  where $logs
  let $rows :=
    for $log in $logs
    return <row time='{ $log/@time }' address='{ $log/@address }' user='{ $log/@user}'
                type='{ $log/@type }' ms='{ $log/@ms }' message='{ $log }'/>
  let $headers := (
    <time type='time' order='desc'>Time</time>,
    <address>Address</address>,
    <user>User</user>,
    <type>Type</type>,
    <ms type='decimal' order='desc'>ms</ms>,
    <message>Message</message>
  )
  return html:table($headers, $rows, (),
    map { 'name': $name, 'loglist': $loglist, 'logs': $query },
    map { 'sort': $sort, 'page': $page }
  )
};

(:~
 : Returns log files matching the specified query.
 : @param  $sort   table sort key
 : @param  $query  search term
 : @return html elements
 :)
declare
  %rest:POST("{$query}")
  %rest:path("/dba/loglist")
  %rest:query-param("sort", "{$sort}")
  %output:method("html")
  %rest:single
function dba:loglist(
  $sort   as xs:string?,
  $query  as xs:string?
) as element()* {
  cons:check(),

  let $logs := try {
    util:eval("for $log in admin:logs()
      let $date := $log/(@date,text())/string()
      where not($query) or (some $entry in admin:logs($date) satisfies matches($entry, $query, 'i'))
      order by $date descending
      return $log", map { 'query': $query }
    )
  } catch * { () }
  where $logs
  let $rows :=
    (: legacy (7.8.1): $a/@date :)
    for $a in $logs
    order by $a descending
    (: legacy (7.8.1): $a/@date :)
    return <row name='{ ($a/(@date,text())) }' size='{ $a/@size }'/>
  let $headers := (
    <name>Name</name>,
    <size type='bytes'>Size</size>
  )
  let $buttons := html:button('delete-logs', 'Delete', true())
  let $link := function($value) { $dba:CAT }
  return html:table($headers, $rows, $buttons, map { 'sort': $sort, 'loglist': $query },
    map { 'link': $link })
};

(:~
 : Redirects to the specified action.
 : @param  $action  action to perform
 : @param  $names   names of selected log files
 :)
declare
  %rest:POST
  %rest:path("/dba/logs")
  %rest:query-param("action", "{$action}")
  %rest:query-param("name",   "{$names}")
  %output:method("html")
function dba:action(
  $action  as xs:string,
  $names   as xs:string*
) {
  web:redirect($action, map { 'name': $names[.], 'redirect': $dba:CAT })
};


Tweaked documentation.

Tweaked documentation.

xquery version "3.0";

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
 :)

(:~
 : This module implements the file API EXPath specification available at http://expath.org/spec/file
 : @author Gabriel Petrovay, Matthias Brantner, Markus Pilman
 : @see http://expath.org/spec/file
 :)
module namespace file = "http://expath.org/ns/file";

import schema namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
declare option ver:module-version "2.0";

(:~
 : Appends a sequence of items to a file. If the file pointed by
$file

 : does not exist, a new file will be created. Before writing to the file, the items
 : are serialized according to the
$serializer-params
.
 :
 : The semantics of
$serializer-params
 is the same as for the
 :
$params
 parameter of the fn:serialize
 : function.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @param $serializer-params Parameter to control the serialization of the
 :    content.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:append(
  $file as xs:string,
  $content as item()*,
  $serializer-params as element(output:serialization-parameters)?
) as empty-sequence()
{
  file:append-text(
    $file,
    fn:serialize($content, $serializer-params))
};

(:~
 : Appends a sequence of Base64 items as binary to a file. If the file pointed
 : by
$file
 does not exist, a new file will be created.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:append-binary(
  $file as xs:string,
  $content as xs:base64Binary*
) as empty-sequence() external;

(:~
 : Appends a sequence of string items to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:append-text(
  $file as xs:string,
  $content as xs:string*
) as empty-sequence() external;

(:~
 : Copies a file or a directory given a source and a destination path/URI.
 :
 : @param $source The path/URI of the file or directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to a file system item
 :    with a different type than the
$source
.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential function file:copy(
  $source as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  if (file:exists($source)) then
    if (file:is-directory($source)) then
      file:copy-directory-impl($source, $destination)
    else
      file:copy-file-impl($source, $destination)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The source path does not exists: ", $source))
};

(:~
 : Copies a file given a source and a destination path/URI.
 :
 : @param $sourceFile The path/URI of the file to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to directory.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL0004 If
$sourceFile
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:copy-file-impl(
  $sourceFile as xs:string,
  $destination as xs:string
) as empty-sequence() external;

(:~
 : Copies a source directory recursively to a destination path/URI.
 :
 : @param $sourceDir The path/URI of the directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential function file:copy-directory-impl(
  $sourceDir as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  if (file:is-file($destination)) then
    fn:error(xs:QName("file:FOFL0002"), fn:concat("The specified destination path already exists: ", $destination))
  else if (fn:not(file:exists($destination))) then
    let $dirname := file:dir-name($destination)
    return
      if (fn:not(file:exists($dirname))) then
        fn:error(xs:QName("file:FOFL0003"), fn:concat("The destination directory does not exist: ", $dirname))
      else
        {
          file:create-directory($destination);
          file:copy-directory-content($sourceDir, $destination)
        }

  else
    let $basename := file:base-name($sourceDir)
    let $newdir := fn:concat($destination, file:directory-separator(), $basename)
    return
      {
        file:create-directory($newdir);
        file:copy-directory-content($sourceDir, $newdir)
      }
};

(:~
 : Copies the content of a given directory to an existing destination
 : directory.
 :
 : @param $sourceDir The path/URI of the directory to copy the content from.
 : @param $destination The destination directory path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0003 If
$destination
 directory does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential function file:copy-directory-content(
  $sourceDir as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  if (file:is-directory($destination)) then
    for $item in file:list($sourceDir)
    let $fullPath := fn:concat($sourceDir, file:directory-separator(), $item)
    return
      file:copy($fullPath, $destination)
  else
    fn:error(xs:QName("file:FOFL0003"), fn:concat("The specified destination directory does not exist: ", $destination))
};

(:~
 : Creates a directory.
 :
 : The operation is will create all the missing parent directories from the
 : given path.
 :
 : @param $dir The path/URI denoting the directory to be created.
 : @return The empty sequence.
 : @error file:FOFL0002 If the directory cannot be created because of an already
 :    existing file.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:create-directory(
  $dir as xs:string
) as empty-sequence() external;

(:~
 : Deletes a file or a directory from the file system.
 :
 : If
$path
 points to a directory the directory will be deleted
 : recursively.
 :
 : @param $path The path/URI of the file or directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$path
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential function file:delete(
  $path as xs:string
) as empty-sequence()
{
  if (file:exists($path)) then
    if (file:is-directory($path)) then
      file:delete-directory-impl($path)
    else
      file:delete-file-impl($path)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The path does not exists: ", $path))
};

(:~
 : Deletes a file from the file system.
 :
 : @param $file The path/URI of the file to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$file
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:delete-file-impl(
  $file as xs:string
) as empty-sequence() external;

(:~
 : Deletes a directory from the file system.
 :
 : @param $dir The path/URI of the directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$dir
 path does not exist.
 : @error file:FOFL0003 If
$dir
 does not point to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential function file:delete-directory-impl(
  $dir as xs:string
) as empty-sequence()
{
  for $item in file:list($dir)
  let $fullPath := fn:concat($dir, file:directory-separator(), $item)
  return
    if (file:is-directory($fullPath)) then
      file:delete-directory-impl($fullPath);
    else
      file:delete-file-impl($fullPath);

  file:delete-file-impl($dir)
};

(:~
 : Tests if a path/URI is already used in the file system.
 :
 : @param $path The path/URI to test for existence.
 : @return true if the path/URI points to an existing file system item.
 :)
declare %an:nondeterministic function file:exists(
  $path as xs:string
) as xs:boolean external;

(:~
 : Tests if a path/URI points to a directory. On UNIX-based systems, the root
 : and the volume roots are considered directories.
 :
 : @param $dir The path/URI to test.
 : @return true if the path/URI points to a directory.
 :)
declare %an:nondeterministic function file:is-directory(
  $dir as xs:string
) as xs:boolean external;

(:~
 : Tests if a path/URI points to a file.
 :
 : @param $dir The path/URI to test.
 : @return true if the path/URI points to a file.
 :)
declare %an:nondeterministic function file:is-file(
  $file as xs:string
) as xs:boolean external;

(:~
 : Moves a file or directory given a source and a destination paths/URIs.
 :
 : @param $source The path/URI of the file to move.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$source
 points to a directory and
 :
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and it's parent
 :    directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:move(
  $source as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  file:copy($source, $destination);
  file:delete($source);
};

(:~
 : Reads the content of a file and returns a Base64 representation of the
 : content.
 :
 : @param $file The file to read.
 : @return The content of the file as Base64.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-binary(
  $file as xs:string
) as xs:base64Binary external;

(:~
 : Reads the content of a file and returns a string representation of the
 : content.
 :
 : The operation is equivalent to calling:
 :
file:read-text($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text(
  $file as xs:string
) as xs:string
{
  file:read-text($file, "UTF-8")
};

(:~
 : Reads the content of a file using the specified encoding and returns a
 : string representation of the content.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text(
  $file as xs:string,
  $encoding as xs:string
) as xs:string external;

(:~
 : Reads the content of a file and returns a sequence of strings representing
 : the lines in the content of the file.
 :
 : The operation is equivalent to calling:
 :
file:read-text-lines($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text-lines(
  $file as xs:string
) as xs:string*
{
  file:read-text-lines($file, "UTF-8")
};

(:~
 : Reads the content of a file using the specified encoding and returns a
 : sequence of strings representing the lines in the content of the file.
 :
 : This implementation considers the LF (
) character as the line
 : separator. If a resulting line ends with the CR (
) character, this is
 : trimmed as well. This implementation will uniformly treat LF and CRLF as
 : line separators.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text-lines(
  $file as xs:string,
  $encoding as xs:string
) as xs:string* external;

(:~
 : This is an internal function that copies an entire source directory to an
 : destination directory. The caller to this function must make sure that both
 : the source and destination point to existing directories.
 :
 : @param $sourceDir The existing source directory.
 : @param $destinationDir The existing destination directory.
 : @return The empty sequence.
 :)
declare %private %an:nondeterministic %an:sequential function file:copy-directory(
  $sourceDir as xs:string,
  $destinationDir as xs:string
) as empty-sequence()
{
  let $name := file:base-name($sourceDir)
  let $destDir := fn:concat($destinationDir, file:directory-separator(), $name)
  return
    {
      file:create-directory($destDir);

      for $item in file:list($sourceDir)
      let $fullSrcPath := fn:concat($sourceDir, file:directory-separator(), $item)
      let $fullDestPath := fn:concat($destDir, file:directory-separator(), $item)
      return
        if (file:is-directory($fullSrcPath)) then
          file:copy-directory($fullSrcPath, $fullDestPath)
        else
          file:copy($fullSrcPath, $fullDestPath)
    }
};

(:~
 : Writes a sequence of items to a file. Before writing to the file, the items
 : are serialized according to the
$serializer-params
.
 :
 : The semantics of
$serializer-params
 is the same as for the
 :
$params
 parameter of the fn:serialize
 : function.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @param $serializer-params Parameter to control the serialization of the
 :        content.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:write(
  $file as xs:string,
  $content as item()*,
  $serializer-params as element(output:serialization-parameters)?
) as empty-sequence()
{
  file:write-text($file, fn:serialize($content, $serializer-params))
};

(:~
 : Writes a sequence of Base64 items as binary to a file.
 :
 : The operation is equivalent to calling:
 :
file:write-binary($file, $content, fn:true())
.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:write-binary(
  $file as xs:string,
  $content as xs:base64Binary*
) as empty-sequence() external;

(:~
 : Writes a sequence of Base64 items as binary to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:write-binary(
  $file as xs:string,
  $content as xs:base64Binary*
) as empty-sequence() external;

(:~
 : Writes a sequence of string items to a file.
 :
 : The operation is equivalent to calling:
 :
file:write-text($file, $content, fn:true())
.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:write-text(
  $file as xs:string,
  $content as xs:string*
) as empty-sequence() external;

(:~
 : Lists the file system items in a certain directory.
 :
 : The operation is equivalent to calling:
 :
file:list($dir, fn:false())
.
 :
 : @param $dir The path/URI of the directory to retrieve the children from.
 : @return The sequence of names of the direct children.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:list(
  $dir as xs:string
) as xs:string* external;

(:~
 : Lists the file system items in a certain directory. The order of the items
 : in the resulting sequence is not defined. The "." and ".." items are not
 : returned. The returned paths are relative to the provided
$path
.
 :
 : If
$recursive
 evaluates to
fn:true()
, the operation
 : will recurse in the subdirectories.
 :
 : @param $dir The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @return A sequence of relative paths.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:list(
  $path as xs:string,
  $recursive as xs:boolean
) as xs:string*
{
  for $f in file:list($path)
  let $full := fn:concat($path, file:directory-separator(), $f)
  return (
    $f,
    if ($recursive and file:is-directory($full)) then
      for $child in file:list($full, $recursive)
      return fn:concat($f, file:directory-separator(), $child)
    else
      ()
  )
};

(:~
 : Lists all files matching the given pattern in a given directory.
 : The order of the items in the result is not defined.
 : The "." and ".." items are not considered for the match.
 : The file paths are relative to the provided $path.
 :
 : The pattern is a glob expression supporting:
 :

 :

*
 for matching any number of unknown characters

 :

?
 for matching one unknown character

 :

 :
 : @param $path The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @param $pattern The file name pattern.
 : @return A sequence of file names matching the pattern.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:list(
  $path as xs:string,
  $recursive as xs:boolean,
  $pattern as xs:string
) as xs:string* {
  for $file in file:list($path, $recursive)
  let $name := fn:tokenize($file, fn:concat("\", file:directory-separator()))[fn:last()]
  return
    if (fn:matches($name, file:glob-to-regex($pattern))) then
      $file
    else
      ()
};

(:~
 : A helper function that performs a trivial (not complete) glob to regex
 : pattern translation.
 :
 : @param $pattern The glob pattern.
 : @return A regex pattern corresponding to the glob pattern provided.
 :)
declare function file:glob-to-regex(
  $pattern as xs:string
) as xs:string {
  let $pattern := fn:replace($pattern, '(\.|\[|\]|\\|/|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
  let $pattern := fn:replace($pattern, '\\\?', '.')
  let $pattern := fn:replace($pattern, '\\\*', '.*')
  return
    fn:concat("^", $pattern, "$")
};

(:~
 : Retrieves the timestamp of the last modification of the file system item
 : pointed by the path/URI.
 :
 : @param $path The file system item to read the last modification
 :    timestamp from.
 : @return The date and time of the last modification of the item.
 : @error file:FOFL0001 If the
$path
 does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:last-modified(
  $path as xs:string
) as xs:dateTime external;

(:~
 : Retrieves the size of a file.
 :
 : @param $file The file get the size.
 : @return An integer representing the size in bytes of the file.
 : @error file:FOFL0001 If the
$file
 does not exist.
 : @error file:FOFL0004 If the
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:size(
  $file as xs:string
) as xs:integer external;

(:~
 : This function returns the value of the operating system specific directory
 : separator. For example,
/
 on UNIX-based systems and
\

 : on Windows systems.
 :
 : @return The operating system specific directory separator.
 :)
declare function file:directory-separator() as xs:string external;

(:~
 : This function returns the value of the operating system specific path
 : separator. For example,
:
 on UNIX-based systems and
;

 : on Windows systems.
 :
 : @return The operating system specific path separator.
 :)
declare function file:path-separator() as xs:string external;

(:~
 : Transforms a relative path/URI into an absolute operating system path by
 : resolving it against the current working directory.
 :
 : No path existence check is made.
 :
 : @param $path The path/URI to transform.
 : @return The operating system file path.
 :)
declare function file:resolve-path(
  $path as xs:string
) as xs:string external;

(:~
 : Transforms a file system path into a URI with the file:// scheme. If the
 : path is relative, it is first resolved against the current working
 : directory.
 :
 : No path existence check is made.
 :
 : @param $path The path to transform.
 : @return The file URI corresponding to
path
.
 :)
declare function file:path-to-uri(
  $path as xs:string
) as xs:anyURI external;

(:~
 : Transforms a URI, an absolute path, or relative path to a native path on the
 : running platform.
 :
 : No path existence check is made.
 :
 : @param $path The uri or path to normalize.
 : @return The native path corresponding to
$path
.
 : @error file:FOFL9999 If an error occurs while trying to obtain the native path.
 :)
declare function file:path-to-native($path as xs:string) as xs:string external;

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters. If
$path
 consists
 : entirely directory-separator characters, the empty string is returned. If
 :
$path
 is the empty string, the string
"."
 is returned,
 : signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path A file path/URI.
 : @return The base name of this file.
 :)
declare function file:base-name($path as xs:string) as xs:string
{
  let $delim := file:directory-separator()
  let $escapedDelim :=
    if ($delim eq "/") then
      $delim
    else
      fn:concat("\", $delim)
  let $normalized-file :=
    let $n := file:prepare-for-dirname-and-base-name($path)
    return if ($delim eq "\" and fn:matches($n, "^[a-zA-Z]:$")) then
      fn:concat($n, "\")
    else $n
  return
    if (fn:matches($path, fn:concat("^", $escapedDelim, "+$"))) then
      ""
    else if ($delim eq "\" and fn:matches($path, "^[a-zA-Z]:\\?$")) then
      ""
    else if ($path eq "") then
      "."
    else
      fn:replace($normalized-file, fn:concat("^.*", $escapedDelim), "")
};

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters and the
$suffix
. If path
 : consists entirely directory-separator characters, the empty string is
 : returned. If path is the empty string, the string
"."
 is
 : returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : The
$suffix
 can be used for example to eliminate file extensions.
 :
 : @param $path A file path/URI.
 : @param $suffix A suffix which should get deleted from the result.
 : @return The base-name of $path with a deleted $suffix.
 :)
declare function file:base-name($path as xs:string, $suffix as xs:string) as xs:string
{
  let $res := file:base-name($path)
  return
    if (fn:ends-with($res, $suffix) and $res ne ".") then
      fn:substring($res, 1, fn:string-length($suffix))
    else
      $res
};

(:~
 : This function is the converse of
file:base-name
. It returns a
 : string denoting the parent directory of the
$path
. Any trailing
 : directory-separator characters are not counted as part of the directory
 : name. If path is the empty string or contains no directory-separator string,
 :
"."
 is returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path The filename, of which the dirname should be get.
 : @return The name of the directory the file is in.
 :)
declare function file:dir-name($path as xs:string) as xs:string
{
  let $delim := file:directory-separator()
  let $escapedDelim :=
    if ($delim eq "/") then
      $delim
    else
      fn:concat("\", $delim)
  let $normalized-file := file:prepare-for-dirname-and-base-name($path)
  return
    if (fn:matches($path, fn:concat("^", $escapedDelim, "+$"))) then
      $delim
    else if ($normalized-file eq $delim) then
      $delim
    else if ($delim eq "\" and fn:matches($path, "^[a-zA-Z]:\\$")) then
      $path
    else if ($delim eq "\" and fn:matches($normalized-file, "^[a-zA-Z]:$")) then
      fn:concat($normalized-file, '\')
    else if ($path eq "") then
      "."
    else if (fn:matches($normalized-file, $escapedDelim)) then
      fn:replace($normalized-file, fn:concat("^(.*)", $escapedDelim, ".*"), "$1")
    else
      "."
};

(:~
 : This is a helper function used by dirname and base-name. This function takes a path as
 : input and normalizes it according to the rules states in dirname/base-name documentation
 : and normalizes it to a system specific path.
 :)
declare %private function file:prepare-for-dirname-and-base-name($path as xs:string) as xs:string
{
  let $delim := file:directory-separator()
  let $escapedDelim :=
    if ($delim eq "/") then
      $delim
    else
      fn:concat("\", $delim)
  let $normalize-path := file:path-to-native($path)
  let $normalized :=
    if ($normalize-path eq $delim) then
      $normalize-path
    else
      fn:replace($normalize-path, fn:concat($escapedDelim, "+$"), "")
  return $normalized
};

xquery version "3.0";

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
 :)

(:~
 : This module implements the file API EXPath specification available at http://expath.org/spec/file
 : @author Gabriel Petrovay, Matthias Brantner, Markus Pilman
 : @see http://expath.org/spec/file
 :)
module namespace file = "http://expath.org/ns/file";

import schema namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
declare option ver:module-version "2.0";

(:~
 : Appends a sequence of items to a file. If the file pointed by
$file

 : does not exist, a new file will be created. Before writing to the file, the items
 : are serialized according to the
$serializer-params
.
 :
 : The semantics of
$serializer-params
 is the same as for the
 :
$params
 parameter of the fn:serialize
 : function.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @param $serializer-params Parameter to control the serialization of the
 :    content.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:append(
  $file as xs:string,
  $content as item()*,
  $serializer-params as element(output:serialization-parameters)?
) as empty-sequence()
{
  file:append-text(
    $file,
    fn:serialize($content, $serializer-params))
};

(:~
 : Appends a sequence of Base64 items as binary to a file. If the file pointed
 : by
$file
 does not exist, a new file will be created.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:append-binary(
  $file as xs:string,
  $content as xs:base64Binary*
) as empty-sequence() external;

(:~
 : Appends a sequence of string items to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:append-text(
  $file as xs:string,
  $content as xs:string*
) as empty-sequence() external;

(:~
 : Copies a file or a directory given a source and a destination path/URI.
 :
 : @param $source The path/URI of the file or directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to a file system item
 :    with a different type than the
$source
.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential function file:copy(
  $source as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  if (file:exists($source)) then
    if (file:is-directory($source)) then
      file:copy-directory-impl($source, $destination)
    else
      file:copy-file-impl($source, $destination)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The source path does not exists: ", $source))
};

(:~
 : Copies a file given a source and a destination path/URI.
 :
 : @param $sourceFile The path/URI of the file to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to directory.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL0004 If
$sourceFile
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:copy-file-impl(
  $sourceFile as xs:string,
  $destination as xs:string
) as empty-sequence() external;

(:~
 : Copies a source directory recursively to a destination path/URI.
 :
 : @param $sourceDir The path/URI of the directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential function file:copy-directory-impl(
  $sourceDir as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  if (file:is-file($destination)) then
    fn:error(xs:QName("file:FOFL0002"), fn:concat("The specified destination path already exists: ", $destination))
  else if (fn:not(file:exists($destination))) then
    let $dirname := file:dir-name($destination)
    return
      if (fn:not(file:exists($dirname))) then
        fn:error(xs:QName("file:FOFL0003"), fn:concat("The destination directory does not exist: ", $dirname))
      else
        {
          file:create-directory($destination);
          file:copy-directory-content($sourceDir, $destination)
        }

  else
    let $basename := file:base-name($sourceDir)
    let $newdir := fn:concat($destination, file:directory-separator(), $basename)
    return
      {
        file:create-directory($newdir);
        file:copy-directory-content($sourceDir, $newdir)
      }
};

(:~
 : Copies the content of a given directory to an existing destination
 : directory.
 :
 : @param $sourceDir The path/URI of the directory to copy the content from.
 : @param $destination The destination directory path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0003 If
$destination
 directory does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential function file:copy-directory-content(
  $sourceDir as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  if (file:is-directory($destination)) then
    for $item in file:list($sourceDir)
    let $fullPath := fn:concat($sourceDir, file:directory-separator(), $item)
    return
      file:copy($fullPath, $destination)
  else
    fn:error(xs:QName("file:FOFL0003"), fn:concat("The specified destination directory does not exist: ", $destination))
};

(:~
 : Creates a directory.
 :
 : The operation is will create all the missing parent directories from the
 : given path.
 :
 : @param $dir The path/URI denoting the directory to be created.
 : @return The empty sequence.
 : @error file:FOFL0002 If the directory cannot be created because of an already
 :    existing file.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:create-directory(
  $dir as xs:string
) as empty-sequence() external;

(:~
 : Deletes a file or a directory from the file system.
 :
 : If
$path
 points to a directory the directory will be deleted
 : recursively.
 :
 : @param $path The path/URI of the file or directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$path
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential function file:delete(
  $path as xs:string
) as empty-sequence()
{
  if (file:exists($path)) then
    if (file:is-directory($path)) then
      file:delete-directory-impl($path)
    else
      file:delete-file-impl($path)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The path does not exists: ", $path))
};

(:~
 : Deletes a file from the file system.
 :
 : @param $file The path/URI of the file to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$file
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:delete-file-impl(
  $file as xs:string
) as empty-sequence() external;

(:~
 : Deletes a directory from the file system.
 :
 : @param $dir The path/URI of the directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$dir
 path does not exist.
 : @error file:FOFL0003 If
$dir
 does not point to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential function file:delete-directory-impl(
  $dir as xs:string
) as empty-sequence()
{
  for $item in file:list($dir)
  let $fullPath := fn:concat($dir, file:directory-separator(), $item)
  return
    if (file:is-directory($fullPath)) then
      file:delete-directory-impl($fullPath);
    else
      file:delete-file-impl($fullPath);

  file:delete-file-impl($dir)
};

(:~
 : Tests if a path/URI is already used in the file system.
 :
 : @param $path The path/URI to test for existence.
 : @return true if the path/URI points to an existing file system item.
 :)
declare %an:nondeterministic function file:exists(
  $path as xs:string
) as xs:boolean external;

(:~
 : Tests if a path/URI points to a directory. On UNIX-based systems, the root
 : and the volume roots are considered directories.
 :
 : @param $dir The path/URI to test.
 : @return true if the path/URI points to a directory.
 :)
declare %an:nondeterministic function file:is-directory(
  $dir as xs:string
) as xs:boolean external;

(:~
 : Tests if a path/URI points to a file.
 :
 : @param $dir The path/URI to test.
 : @return true if the path/URI points to a file.
 :)
declare %an:nondeterministic function file:is-file(
  $file as xs:string
) as xs:boolean external;

(:~
 : Moves a file or directory given a source and a destination paths/URIs.
 :
 : @param $source The path/URI of the file to move.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$source
 points to a directory and
 :
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and it's parent
 :    directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:move(
  $source as xs:string,
  $destination as xs:string
) as empty-sequence()
{
  file:copy($source, $destination);
  file:delete($source);
};

(:~
 : Reads the content of a file and returns a Base64 representation of the
 : content.
 :
 : @param $file The file to read.
 : @return The content of the file as Base64.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-binary(
  $file as xs:string
) as xs:base64Binary external;

(:~
 : Reads the content of a file and returns a string representation of the
 : content.
 :
 : The operation is equivalent to calling:
 :
file:read-text($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text(
  $file as xs:string
) as xs:string
{
  file:read-text($file, "UTF-8")
};

(:~
 : Reads the content of a file using the specified encoding and returns a
 : string representation of the content.
 :
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text(
  $file as xs:string,
  $encoding as xs:string
) as xs:string external;

(:~
 : Reads the content of a file and returns a sequence of strings representing
 : the lines in the content of the file.
 :
 : The operation is equivalent to calling:
 :
file:read-text-lines($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text-lines(
  $file as xs:string
) as xs:string*
{
  file:read-text-lines($file, "UTF-8")
};

(:~
 : Reads the content of a file using the specified encoding and returns a
 : sequence of strings representing the lines in the content of the file.
 :
 : This implementation considers the LF (
) character as the line
 : separator. If a resulting line ends with the CR (
) character, this is
 : trimmed as well. This implementation will uniformly treat LF and CRLF as
 : line separators.
 :
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:read-text-lines(
  $file as xs:string,
  $encoding as xs:string
) as xs:string* external;

(:~
 : This is an internal function that copies an entire source directory to an
 : destination directory. The caller to this function must make sure that both
 : the source and destination point to existing directories.
 :
 : @param $sourceDir The existing source directory.
 : @param $destinationDir The existing destination directory.
 : @return The empty sequence.
 :)
declare %private %an:nondeterministic %an:sequential function file:copy-directory(
  $sourceDir as xs:string,
  $destinationDir as xs:string
) as empty-sequence()
{
  let $name := file:base-name($sourceDir)
  let $destDir := fn:concat($destinationDir, file:directory-separator(), $name)
  return
    {
      file:create-directory($destDir);

      for $item in file:list($sourceDir)
      let $fullSrcPath := fn:concat($sourceDir, file:directory-separator(), $item)
      let $fullDestPath := fn:concat($destDir, file:directory-separator(), $item)
      return
        if (file:is-directory($fullSrcPath)) then
          file:copy-directory($fullSrcPath, $fullDestPath)
        else
          file:copy($fullSrcPath, $fullDestPath)
    }
};

(:~
 : Writes a sequence of items to a file. Before writing to the file, the items
 : are serialized according to the
$serializer-params
.
 :
 : The semantics of
$serializer-params
 is the same as for the
 :
$params
 parameter of the fn:serialize
 : function.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @param $serializer-params Parameter to control the serialization of the
 :        content.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:write(
  $file as xs:string,
  $content as item()*,
  $serializer-params as element(output:serialization-parameters)?
) as empty-sequence()
{
  file:write-text($file, fn:serialize($content, $serializer-params))
};

(:~
 : Writes a sequence of Base64 items as binary to a file.
 :
 : The operation is equivalent to calling:
 :
file:write-binary($file, $content, fn:true())
.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:write-binary(
  $file as xs:string,
  $content as xs:base64Binary*
) as empty-sequence() external;

(:~
 : Writes a sequence of Base64 items as binary to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function file:write-binary(
  $file as xs:string,
  $content as xs:base64Binary*
) as empty-sequence() external;

(:~
 : Writes a sequence of string items to a file.
 :
 : The operation is equivalent to calling:
 :
file:write-text($file, $content, fn:true())
.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be serialized to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential function file:write-text(
  $file as xs:string,
  $content as xs:string*
) as empty-sequence() external;

(:~
 : Lists the file system items in a certain directory.
 :
 : The operation is equivalent to calling:
 :
file:list($dir, fn:false())
.
 :
 : @param $dir The path/URI of the directory to retrieve the children from.
 : @return The sequence of names of the direct children.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:list(
  $dir as xs:string
) as xs:string* external;

(:~
 : Lists the file system items in a certain directory. The order of the items
 : in the resulting sequence is not defined. The "." and ".." items are not
 : returned. The returned paths are relative to the provided
$path
.
 :
 : If
$recursive
 evaluates to
fn:true()
, the operation
 : will recurse in the subdirectories.
 :
 : @param $dir The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @return A sequence of relative paths.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:list(
  $path as xs:string,
  $recursive as xs:boolean
) as xs:string*
{
  for $f in file:list($path)
  let $full := fn:concat($path, file:directory-separator(), $f)
  return (
    $f,
    if ($recursive and file:is-directory($full)) then
      for $child in file:list($full, $recursive)
      return fn:concat($f, file:directory-separator(), $child)
    else
      ()
  )
};

(:~
 : Lists all files matching the given pattern in a given directory.
 : The order of the items in the result is not defined.
 : The "." and ".." items are not considered for the match.
 : The file paths are relative to the provided $path.
 :
 : The pattern is a glob expression supporting:
 :

 :

*
 for matching any number of unknown characters

 :

?
 for matching one unknown character

 :

 :
 : @param $path The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @param $pattern The file name pattern.
 : @return A sequence of file names matching the pattern.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:list(
  $path as xs:string,
  $recursive as xs:boolean,
  $pattern as xs:string
) as xs:string* {
  for $file in file:list($path, $recursive)
  let $name := fn:tokenize($file, fn:concat("\", file:directory-separator()))[fn:last()]
  return
    if (fn:matches($name, file:glob-to-regex($pattern))) then
      $file
    else
      ()
};

(:~
 : A helper function that performs a trivial (not complete) glob to regex
 : pattern translation.
 :
 : @param $pattern The glob pattern.
 : @return A regex pattern corresponding to the glob pattern provided.
 :)
declare function file:glob-to-regex(
  $pattern as xs:string
) as xs:string {
  let $pattern := fn:replace($pattern, '(\.|\[|\]|\\|/|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
  let $pattern := fn:replace($pattern, '\\\?', '.')
  let $pattern := fn:replace($pattern, '\\\*', '.*')
  return
    fn:concat("^", $pattern, "$")
};

(:~
 : Retrieves the timestamp of the last modification of the file system item
 : pointed by the path/URI.
 :
 : @param $path The file system item to read the last modification
 :    timestamp from.
 : @return The date and time of the last modification of the item.
 : @error file:FOFL0001 If the
$path
 does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:last-modified(
  $path as xs:string
) as xs:dateTime external;

(:~
 : Retrieves the size of a file.
 :
 : @param $file The file get the size.
 : @return An integer representing the size in bytes of the file.
 : @error file:FOFL0001 If the
$file
 does not exist.
 : @error file:FOFL0004 If the
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:size(
  $file as xs:string
) as xs:integer external;

(:~
 : This function returns the value of the operating system specific directory
 : separator. For example,
/
 on UNIX-based systems and
\

 : on Windows systems.
 :
 : @return The operating system specific directory separator.
 :)
declare function file:directory-separator() as xs:string external;

(:~
 : This function returns the value of the operating system specific path
 : separator. For example,
:
 on UNIX-based systems and
;

 : on Windows systems.
 :
 : @return The operating system specific path separator.
 :)
declare function file:path-separator() as xs:string external;

(:~
 : Transforms a relative path/URI into an absolute operating system path by
 : resolving it against the current working directory.
 :
 : No path existence check is made.
 :
 : @param $path The path/URI to transform.
 : @return The operating system file path.
 :)
declare function file:resolve-path(
  $path as xs:string
) as xs:string external;

(:~
 : Transforms a file system path into a URI with the file:// scheme. If the
 : path is relative, it is first resolved against the current working
 : directory.
 :
 : No path existence check is made.
 :
 : @param $path The path to transform.
 : @return The file URI corresponding to
path
.
 :)
declare function file:path-to-uri(
  $path as xs:string
) as xs:anyURI external;

(:~
 : Transforms a URI, an absolute path, or relative path to a native path on the
 : running platform.
 :
 : No path existence check is made.
 :
 : @param $path The uri or path to normalize.
 : @return The native path corresponding to
$path
.
 : @error file:FOFL9999 If an error occurs while trying to obtain the native path.
 :)
declare function file:path-to-native($path as xs:string) as xs:string external;

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters. If
$path
 consists
 : entirely directory-separator characters, the empty string is returned. If
 :
$path
 is the empty string, the string
"."
 is returned,
 : signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path A file path/URI.
 : @return The base name of this file.
 :)
declare function file:base-name($path as xs:string) as xs:string
{
  let $delim := file:directory-separator()
  let $escapedDelim :=
    if ($delim eq "/") then
      $delim
    else
      fn:concat("\", $delim)
  let $normalized-file :=
    let $n := file:prepare-for-dirname-and-base-name($path)
    return if ($delim eq "\" and fn:matches($n, "^[a-zA-Z]:$")) then
      fn:concat($n, "\")
    else $n
  return
    if (fn:matches($path, fn:concat("^", $escapedDelim, "+$"))) then
      ""
    else if ($delim eq "\" and fn:matches($path, "^[a-zA-Z]:\\?$")) then
      ""
    else if ($path eq "") then
      "."
    else
      fn:replace($normalized-file, fn:concat("^.*", $escapedDelim), "")
};

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters and the
$suffix
. If path
 : consists entirely directory-separator characters, the empty string is
 : returned. If path is the empty string, the string
"."
 is
 : returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : The
$suffix
 can be used for example to eliminate file extensions.
 :
 : @param $path A file path/URI.
 : @param $suffix A suffix which should get deleted from the result.
 : @return The base-name of $path with a deleted $suffix.
 :)
declare function file:base-name($path as xs:string, $suffix as xs:string) as xs:string
{
  let $res := file:base-name($path)
  return
    if (fn:ends-with($res, $suffix) and $res ne ".") then
      fn:substring($res, 1, fn:string-length($suffix))
    else
      $res
};

(:~
 : This function is the converse of
file:base-name
. It returns a
 : string denoting the parent directory of the
$path
. Any trailing
 : directory-separator characters are not counted as part of the directory
 : name. If path is the empty string or contains no directory-separator string,
 :
"."
 is returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path The filename, of which the dirname should be get.
 : @return The name of the directory the file is in.
 :)
declare function file:dir-name($path as xs:string) as xs:string
{
  let $delim := file:directory-separator()
  let $escapedDelim :=
    if ($delim eq "/") then
      $delim
    else
      fn:concat("\", $delim)
  let $normalized-file := file:prepare-for-dirname-and-base-name($path)
  return
    if (fn:matches($path, fn:concat("^", $escapedDelim, "+$"))) then
      $delim
    else if ($normalized-file eq $delim) then
      $delim
    else if ($delim eq "\" and fn:matches($path, "^[a-zA-Z]:\\$")) then
      $path
    else if ($delim eq "\" and fn:matches($normalized-file, "^[a-zA-Z]:$")) then
      fn:concat($normalized-file, '\')
    else if ($path eq "") then
      "."
    else if (fn:matches($normalized-file, $escapedDelim)) then
      fn:replace($normalized-file, fn:concat("^(.*)", $escapedDelim, ".*"), "$1")
    else
      "."
};

(:~
 : This is a helper function used by dirname and base-name. This function takes a path as
 : input and normalizes it according to the rules states in dirname/base-name documentation
 : and normalizes it to a system specific path.
 :)
declare %private function file:prepare-for-dirname-and-base-name($path as xs:string) as xs:string
{
  let $delim := file:directory-separator()
  let $escapedDelim :=
    if ($delim eq "/") then
      $delim
    else
      fn:concat("\", $delim)
  let $normalize-path := file:path-to-native($path)
  let $normalized :=
    if ($normalize-path eq $delim) then
      $normalize-path
    else
      fn:replace($normalize-path, fn:concat($escapedDelim, "+$"), "")
  return $normalized
};


fix typo in csd_webapp_config.xqm - does not impact Ubuntu packaging

(:~
: This is a module conttaining configuration options for the openinfoman webap
: @version 1.0
: @see https://github.com/openhie/openinfoman @see http://ihe.net
:
:)

module namespace csd_webconf = "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace request = "http://exquery.org/ns/request";

(:Database we are working on:)
declare variable $csd_webconf:db :=  'provider_directory';

(:BASE URL for openinforman. Overwrite this if you are proxying the openinfoman.   :)
declare variable $csd_webconf:baseurl :=  concat(request:scheme(),"://",request:hostname(),":",request:port(), "/");

declare variable $csd_webconf:remote_services := ();